<commit_before>/*
Copyright 2015 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 leaderelection implements leader election of a set of endpoints.
// It uses an annotation in the endpoints object to store the record of the
// election state. This implementation does not guarantee that only one
// client is acting as a leader (a.k.a. fencing).
//
// A client observes timestamps captured locally to infer the state of the
// leader election. Thus the implementation is tolerant to arbitrary clock
// skew, but is not tolerant to arbitrary clock skew rate. Timestamp(renew time)
// is not meaningful if it was collected on another machine. The implementation
// of this client only acts on locally collected timestamps and cannot rely on
// the accuracy of timestamp in the record for correctness.
//
// However the level of tolerance to skew rate can be configured by setting
// RenewDeadline and LeaseDuration appropriately. The tolerance expressed as a
// maximum tolerated ratio of time passed on the fastest node to time passed on
// the slowest node can be approximately achieved with a configuration that sets
// the same ratio of LeaseDuration to RenewDeadline. For example if a user wanted
// to tolerate some nodes progressing forward in time twice as fast as other nodes,
// the user could set LeaseDuration to 60 seconds and RenewDeadline to 30 seconds.
//
// While not required, some method of clock synchronization between nodes in the
// cluster is highly recommended. It's important to keep in mind when configuring
// this client that the tolerance to skew rate varies inversely to master
// availability.
//
// Larger clusters often have a more lenient SLA for API latency. This should be
// taken into account when configuring the client. The rate of leader transitions
// should be monitored and RetryPeriod and LeaseDuration should be increased
// until the rate is stable and acceptably low. It's important to keep in mind
// when configuring this client that the tolerance to API latency varies inversely
// to master availability.
//
// DISCLAIMER: this is an alpha API. This library will likely change significantly
// or even be removed entirely in subsequent releases. Depend on this API at
// your own risk.
package leaderelection

import (
	"context"
	"fmt"
	"reflect"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	rl "k8s.io/client-go/tools/leaderelection/resourcelock"

	"k8s.io/klog"
)

const (
	JitterFactor = 1.2
)

// NewLeaderElector creates a LeaderElector from a LeaderElectionConfig
func NewLeaderElector(lec LeaderElectionConfig) (*LeaderElector, error) {
	if lec.LeaseDuration <= lec.RenewDeadline {
		return nil, fmt.Errorf("leaseDuration must be greater than renewDeadline")
	}
	if lec.RenewDeadline <= time.Duration(JitterFactor*float64(lec.RetryPeriod)) {
		return nil, fmt.Errorf("renewDeadline must be greater than retryPeriod*JitterFactor")
	}
	if lec.LeaseDuration < 1 {
		return nil, fmt.Errorf("leaseDuration must be greater than zero")
	}
	if lec.RenewDeadline < 1 {
		return nil, fmt.Errorf("renewDeadline must be greater than zero")
	}
	if lec.RetryPeriod < 1 {
		return nil, fmt.Errorf("retryPeriod must be greater than zero")
	}

	if lec.Lock == nil {
		return nil, fmt.Errorf("Lock must not be nil.")
	}
	le := LeaderElector{
		config:  lec,
		clock:   clock.RealClock{},
		metrics: globalMetricsFactory.newLeaderMetrics(),
	}
	le.metrics.leaderOff(le.config.Name)
	return &le, nil
}

type LeaderElectionConfig struct {
	// Lock is the resource that will be used for locking
	Lock rl.Interface

	// LeaseDuration is the duration that non-leader candidates will
	// wait to force acquire leadership. This is measured against time of
	// last observed ack. A client needs to wait a full LeaseDuration without
	// observing a change to the record before it can attempt to take over even
	// when a client with a different identity against the record's starts and
	// the renew time in the record is older than LeaseDuration. A.k.a., when
	// all clients are shutdown and after at least a LeaseDuration, clients
	// started with different identities against the record's must wait a full
	// LeaseDuration before acquiring a lock. Thus LeaseDuration should be as
	// short as possible to avoid a possible long waiting. LeaseDuration is 15
	// seconds in core Kubernetes components.
	LeaseDuration time.Duration
	// RenewDeadline is the duration that the acting master will retry
	// refreshing leadership before giving up.
	RenewDeadline time.Duration
	// RetryPeriod is the duration the LeaderElector clients should wait
	// between tries of actions.
	RetryPeriod time.Duration

	// Callbacks are callbacks that are triggered during certain lifecycle
	// events of the LeaderElector
	Callbacks LeaderCallbacks

	// WatchDog is the associated health checker
	// WatchDog may be null if its not needed/configured.
	WatchDog *HealthzAdaptor

	// ReleaseOnCancel should be set true if the lock should be released
	// when the run context is cancelled. If you set this to true, you must
	// ensure all code guarded by this lease has successfully completed
	// prior to cancelling the context, or you may have two processes
	// simultaneously acting on the critical path.
	ReleaseOnCancel bool

	// Name is the name of the resource lock for debugging
	Name string
}

// LeaderCallbacks are callbacks that are triggered during certain
// lifecycle events of the LeaderElector. These are invoked asynchronously.
//
// possible future callbacks:
//  * OnChallenge()
type LeaderCallbacks struct {
	// OnStartedLeading is called when a LeaderElector client starts leading
	OnStartedLeading func(context.Context)
	// OnStoppedLeading is called when a LeaderElector client stops leading
	OnStoppedLeading func()
	// OnNewLeader is called when the client observes a leader that is
	// not the previously observed leader. This includes the first observed
	// leader when the client starts.
	OnNewLeader func(identity string)
}

// LeaderElector is a leader election client.
type LeaderElector struct {
	config LeaderElectionConfig
	// internal bookkeeping
	observedRecord rl.LeaderElectionRecord
	observedTime   time.Time
	// used to implement OnNewLeader(), may lag slightly from the
	// value observedRecord.HolderIdentity if the transition has
	// not yet been reported.
	reportedLeader string

	// clock is wrapper around time to allow for less flaky testing
	clock clock.Clock

	metrics leaderMetricsAdapter

	// name is the name of the resource lock for debugging
	name string
}

// Run starts the leader election loop
func (le *LeaderElector) Run(ctx context.Context) {
	defer func() {
		runtime.HandleCrash()
		le.config.Callbacks.OnStoppedLeading()
	}()
	if !le.acquire(ctx) {
		return // ctx signalled done
	}
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	go le.config.Callbacks.OnStartedLeading(ctx)
	le.renew(ctx)
}

// RunOrDie starts a client with the provided config or panics if the config
// fails to validate.
func RunOrDie(ctx context.Context, lec LeaderElectionConfig) {
	le, err := NewLeaderElector(lec)
	if err != nil {
		panic(err)
	}
	if lec.WatchDog != nil {
		lec.WatchDog.SetLeaderElection(le)
	}
	le.Run(ctx)
}

// GetLeader returns the identity of the last observed leader or returns the empty string if
// no leader has yet been observed.
func (le *LeaderElector) GetLeader() string {
	return le.observedRecord.HolderIdentity
}

// IsLeader returns true if the last observed leader was this client else returns false.
func (le *LeaderElector) IsLeader() bool {
	return le.observedRecord.HolderIdentity == le.config.Lock.Identity()
}

// acquire loops calling tryAcquireOrRenew and returns true immediately when tryAcquireOrRenew succeeds.
// Returns false if ctx signals done.
func (le *LeaderElector) acquire(ctx context.Context) bool {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	succeeded := false
	desc := le.config.Lock.Describe()
	klog.Infof("attempting to acquire leader lease  %v...", desc)
	wait.JitterUntil(func() {
		succeeded = le.tryAcquireOrRenew()
		le.maybeReportTransition()
		if !succeeded {
			klog.V(4).Infof("failed to acquire lease %v", desc)
			return
		}
		le.config.Lock.RecordEvent("became leader")
		le.metrics.leaderOn(le.config.Name)
		klog.Infof("successfully acquired lease %v", desc)
		cancel()
	}, le.config.RetryPeriod, JitterFactor, true, ctx.Done())
	return succeeded
}

// renew loops calling tryAcquireOrRenew and returns immediately when tryAcquireOrRenew fails or ctx signals done.
func (le *LeaderElector) renew(ctx context.Context) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	wait.Until(func() {
		timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RenewDeadline)
		defer timeoutCancel()
		err := wait.PollImmediateUntil(le.config.RetryPeriod, func() (bool, error) {
			done := make(chan bool, 1)
			go func() {
				defer close(done)
				done <- le.tryAcquireOrRenew()
			}()

			select {
			case <-timeoutCtx.Done():
				return false, fmt.Errorf("failed to tryAcquireOrRenew %s", timeoutCtx.Err())
			case result := <-done:
				return result, nil
			}
		}, timeoutCtx.Done())

		le.maybeReportTransition()
		desc := le.config.Lock.Describe()
		if err == nil {
			klog.V(5).Infof("successfully renewed lease %v", desc)
			return
		}
		le.config.Lock.RecordEvent("stopped leading")
		le.metrics.leaderOff(le.config.Name)
		klog.Infof("failed to renew lease %v: %v", desc, err)
		cancel()
	}, le.config.RetryPeriod, ctx.Done())

	// if we hold the lease, give it up
	if le.config.ReleaseOnCancel {
		le.release()
	}
}

// release attempts to release the leader lease if we have acquired it.
func (le *LeaderElector) release() bool {
	if !le.IsLeader() {
		return true
	}
	leaderElectionRecord := rl.LeaderElectionRecord{
		LeaderTransitions: le.observedRecord.LeaderTransitions,
	}
	if err := le.config.Lock.Update(leaderElectionRecord); err != nil {
		klog.Errorf("Failed to release lock: %v", err)
		return false
	}
	le.observedRecord = leaderElectionRecord
	le.observedTime = le.clock.Now()
	return true
}

// tryAcquireOrRenew tries to acquire a leader lease if it is not already acquired,
// else it tries to renew the lease if it has already been acquired. Returns true
// on success else returns false.
func (le *LeaderElector) tryAcquireOrRenew() bool {
	now := metav1.Now()
	leaderElectionRecord := rl.LeaderElectionRecord{
		HolderIdentity:       le.config.Lock.Identity(),
		LeaseDurationSeconds: int(le.config.LeaseDuration / time.Second),
		RenewTime:            now,
		AcquireTime:          now,
	}

	// 1. obtain or create the ElectionRecord
	oldLeaderElectionRecord, err := le.config.Lock.Get()
	if err != nil {
		if !errors.IsNotFound(err) {
			klog.Errorf("error retrieving resource lock %v: %v", le.config.Lock.Describe(), err)
			return false
		}
		if err = le.config.Lock.Create(leaderElectionRecord); err != nil {
			klog.Errorf("error initially creating leader election record: %v", err)
			return false
		}
		le.observedRecord = leaderElectionRecord
		le.observedTime = le.clock.Now()
		return true
	}

	// 2. Record obtained, check the Identity & Time
	if !reflect.DeepEqual(le.observedRecord, *oldLeaderElectionRecord) {
		le.observedRecord = *oldLeaderElectionRecord
		le.observedTime = le.clock.Now()
	}
	if len(oldLeaderElectionRecord.HolderIdentity) > 0 &&
		le.observedTime.Add(le.config.LeaseDuration).After(now.Time) &&
		!le.IsLeader() {
		klog.V(4).Infof("lock is held by %v and has not yet expired", oldLeaderElectionRecord.HolderIdentity)
		return false
	}

	// 3. We're going to try to update. The leaderElectionRecord is set to it's default
	// here. Let's correct it before updating.
	if le.IsLeader() {
		leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime
		leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions
	} else {
		leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1
	}

	// update the lock itself
	if err = le.config.Lock.Update(leaderElectionRecord); err != nil {
		klog.Errorf("Failed to update lock: %v", err)
		return false
	}
	le.observedRecord = leaderElectionRecord
	le.observedTime = le.clock.Now()
	return true
}

func (le *LeaderElector) maybeReportTransition() {
	if le.observedRecord.HolderIdentity == le.reportedLeader {
		return
	}
	le.reportedLeader = le.observedRecord.HolderIdentity
	if le.config.Callbacks.OnNewLeader != nil {
		go le.config.Callbacks.OnNewLeader(le.reportedLeader)
	}
}

// Check will determine if the current lease is expired by more than timeout.
func (le *LeaderElector) Check(maxTolerableExpiredLease time.Duration) error {
	if !le.IsLeader() {
		// Currently not concerned with the case that we are hot standby
		return nil
	}
	// If we are more than timeout seconds after the lease duration that is past the timeout
	// on the lease renew. Time to start reporting ourselves as unhealthy. We should have
	// died but conditions like deadlock can prevent this. (See #70819)
	if le.clock.Since(le.observedTime) > le.config.LeaseDuration+maxTolerableExpiredLease {
		return fmt.Errorf("failed election to renew leadership on lease %s", le.config.Name)
	}

	return nil
}
<commit_msg>cleanup some leader election client doc<commit_after>/*
Copyright 2015 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 leaderelection implements leader election of a set of endpoints.
// It uses an annotation in the endpoints object to store the record of the
// election state. This implementation does not guarantee that only one
// client is acting as a leader (a.k.a. fencing).
//
// A client only acts on timestamps captured locally to infer the state of the
// leader election. The client does not consider timestamps in the leader
// election record to be accurate because these timestamps may not have been
// produced by a local clock. The implemention does not depend on their
// accuracy and only uses their change to indicate that another client has
// renewed the leader lease. Thus the implementation is tolerant to arbitrary
// clock skew, but is not tolerant to arbitrary clock skew rate.
//
// However the level of tolerance to skew rate can be configured by setting
// RenewDeadline and LeaseDuration appropriately. The tolerance expressed as a
// maximum tolerated ratio of time passed on the fastest node to time passed on
// the slowest node can be approximately achieved with a configuration that sets
// the same ratio of LeaseDuration to RenewDeadline. For example if a user wanted
// to tolerate some nodes progressing forward in time twice as fast as other nodes,
// the user could set LeaseDuration to 60 seconds and RenewDeadline to 30 seconds.
//
// While not required, some method of clock synchronization between nodes in the
// cluster is highly recommended. It's important to keep in mind when configuring
// this client that the tolerance to skew rate varies inversely to master
// availability.
//
// Larger clusters often have a more lenient SLA for API latency. This should be
// taken into account when configuring the client. The rate of leader transitions
// should be monitored and RetryPeriod and LeaseDuration should be increased
// until the rate is stable and acceptably low. It's important to keep in mind
// when configuring this client that the tolerance to API latency varies inversely
// to master availability.
//
// DISCLAIMER: this is an alpha API. This library will likely change significantly
// or even be removed entirely in subsequent releases. Depend on this API at
// your own risk.
package leaderelection

import (
	"context"
	"fmt"
	"reflect"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	rl "k8s.io/client-go/tools/leaderelection/resourcelock"

	"k8s.io/klog"
)

const (
	JitterFactor = 1.2
)

// NewLeaderElector creates a LeaderElector from a LeaderElectionConfig
func NewLeaderElector(lec LeaderElectionConfig) (*LeaderElector, error) {
	if lec.LeaseDuration <= lec.RenewDeadline {
		return nil, fmt.Errorf("leaseDuration must be greater than renewDeadline")
	}
	if lec.RenewDeadline <= time.Duration(JitterFactor*float64(lec.RetryPeriod)) {
		return nil, fmt.Errorf("renewDeadline must be greater than retryPeriod*JitterFactor")
	}
	if lec.LeaseDuration < 1 {
		return nil, fmt.Errorf("leaseDuration must be greater than zero")
	}
	if lec.RenewDeadline < 1 {
		return nil, fmt.Errorf("renewDeadline must be greater than zero")
	}
	if lec.RetryPeriod < 1 {
		return nil, fmt.Errorf("retryPeriod must be greater than zero")
	}

	if lec.Lock == nil {
		return nil, fmt.Errorf("Lock must not be nil.")
	}
	le := LeaderElector{
		config:  lec,
		clock:   clock.RealClock{},
		metrics: globalMetricsFactory.newLeaderMetrics(),
	}
	le.metrics.leaderOff(le.config.Name)
	return &le, nil
}

type LeaderElectionConfig struct {
	// Lock is the resource that will be used for locking
	Lock rl.Interface

	// LeaseDuration is the duration that non-leader candidates will
	// wait to force acquire leadership. This is measured against time of
	// last observed ack.
	//
	// A client needs to wait a full LeaseDuration without observing a change to
	// the record before it can attempt to take over. When all clients are
	// shutdown and a new set of clients are started with different names against
	// the same leader record, they must wait the full LeaseDuration before
	// attempting to acquire the lease. Thus LeaseDuration should be as short as
	// possible (within your tolerance for clock skew rate) to avoid a possible
	// long waits in the scenario.
	//
	// Core clients default this value to 15 seconds.
	LeaseDuration time.Duration
	// RenewDeadline is the duration that the acting master will retry
	// refreshing leadership before giving up.
	//
	// Core clients default this value to 10 seconds.
	RenewDeadline time.Duration
	// RetryPeriod is the duration the LeaderElector clients should wait
	// between tries of actions.
	//
	// Core clients default this value to 2 seconds.
	RetryPeriod time.Duration

	// Callbacks are callbacks that are triggered during certain lifecycle
	// events of the LeaderElector
	Callbacks LeaderCallbacks

	// WatchDog is the associated health checker
	// WatchDog may be null if its not needed/configured.
	WatchDog *HealthzAdaptor

	// ReleaseOnCancel should be set true if the lock should be released
	// when the run context is cancelled. If you set this to true, you must
	// ensure all code guarded by this lease has successfully completed
	// prior to cancelling the context, or you may have two processes
	// simultaneously acting on the critical path.
	ReleaseOnCancel bool

	// Name is the name of the resource lock for debugging
	Name string
}

// LeaderCallbacks are callbacks that are triggered during certain
// lifecycle events of the LeaderElector. These are invoked asynchronously.
//
// possible future callbacks:
//  * OnChallenge()
type LeaderCallbacks struct {
	// OnStartedLeading is called when a LeaderElector client starts leading
	OnStartedLeading func(context.Context)
	// OnStoppedLeading is called when a LeaderElector client stops leading
	OnStoppedLeading func()
	// OnNewLeader is called when the client observes a leader that is
	// not the previously observed leader. This includes the first observed
	// leader when the client starts.
	OnNewLeader func(identity string)
}

// LeaderElector is a leader election client.
type LeaderElector struct {
	config LeaderElectionConfig
	// internal bookkeeping
	observedRecord rl.LeaderElectionRecord
	observedTime   time.Time
	// used to implement OnNewLeader(), may lag slightly from the
	// value observedRecord.HolderIdentity if the transition has
	// not yet been reported.
	reportedLeader string

	// clock is wrapper around time to allow for less flaky testing
	clock clock.Clock

	metrics leaderMetricsAdapter

	// name is the name of the resource lock for debugging
	name string
}

// Run starts the leader election loop
func (le *LeaderElector) Run(ctx context.Context) {
	defer func() {
		runtime.HandleCrash()
		le.config.Callbacks.OnStoppedLeading()
	}()
	if !le.acquire(ctx) {
		return // ctx signalled done
	}
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	go le.config.Callbacks.OnStartedLeading(ctx)
	le.renew(ctx)
}

// RunOrDie starts a client with the provided config or panics if the config
// fails to validate.
func RunOrDie(ctx context.Context, lec LeaderElectionConfig) {
	le, err := NewLeaderElector(lec)
	if err != nil {
		panic(err)
	}
	if lec.WatchDog != nil {
		lec.WatchDog.SetLeaderElection(le)
	}
	le.Run(ctx)
}

// GetLeader returns the identity of the last observed leader or returns the empty string if
// no leader has yet been observed.
func (le *LeaderElector) GetLeader() string {
	return le.observedRecord.HolderIdentity
}

// IsLeader returns true if the last observed leader was this client else returns false.
func (le *LeaderElector) IsLeader() bool {
	return le.observedRecord.HolderIdentity == le.config.Lock.Identity()
}

// acquire loops calling tryAcquireOrRenew and returns true immediately when tryAcquireOrRenew succeeds.
// Returns false if ctx signals done.
func (le *LeaderElector) acquire(ctx context.Context) bool {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	succeeded := false
	desc := le.config.Lock.Describe()
	klog.Infof("attempting to acquire leader lease  %v...", desc)
	wait.JitterUntil(func() {
		succeeded = le.tryAcquireOrRenew()
		le.maybeReportTransition()
		if !succeeded {
			klog.V(4).Infof("failed to acquire lease %v", desc)
			return
		}
		le.config.Lock.RecordEvent("became leader")
		le.metrics.leaderOn(le.config.Name)
		klog.Infof("successfully acquired lease %v", desc)
		cancel()
	}, le.config.RetryPeriod, JitterFactor, true, ctx.Done())
	return succeeded
}

// renew loops calling tryAcquireOrRenew and returns immediately when tryAcquireOrRenew fails or ctx signals done.
func (le *LeaderElector) renew(ctx context.Context) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	wait.Until(func() {
		timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RenewDeadline)
		defer timeoutCancel()
		err := wait.PollImmediateUntil(le.config.RetryPeriod, func() (bool, error) {
			done := make(chan bool, 1)
			go func() {
				defer close(done)
				done <- le.tryAcquireOrRenew()
			}()

			select {
			case <-timeoutCtx.Done():
				return false, fmt.Errorf("failed to tryAcquireOrRenew %s", timeoutCtx.Err())
			case result := <-done:
				return result, nil
			}
		}, timeoutCtx.Done())

		le.maybeReportTransition()
		desc := le.config.Lock.Describe()
		if err == nil {
			klog.V(5).Infof("successfully renewed lease %v", desc)
			return
		}
		le.config.Lock.RecordEvent("stopped leading")
		le.metrics.leaderOff(le.config.Name)
		klog.Infof("failed to renew lease %v: %v", desc, err)
		cancel()
	}, le.config.RetryPeriod, ctx.Done())

	// if we hold the lease, give it up
	if le.config.ReleaseOnCancel {
		le.release()
	}
}

// release attempts to release the leader lease if we have acquired it.
func (le *LeaderElector) release() bool {
	if !le.IsLeader() {
		return true
	}
	leaderElectionRecord := rl.LeaderElectionRecord{
		LeaderTransitions: le.observedRecord.LeaderTransitions,
	}
	if err := le.config.Lock.Update(leaderElectionRecord); err != nil {
		klog.Errorf("Failed to release lock: %v", err)
		return false
	}
	le.observedRecord = leaderElectionRecord
	le.observedTime = le.clock.Now()
	return true
}

// tryAcquireOrRenew tries to acquire a leader lease if it is not already acquired,
// else it tries to renew the lease if it has already been acquired. Returns true
// on success else returns false.
func (le *LeaderElector) tryAcquireOrRenew() bool {
	now := metav1.Now()
	leaderElectionRecord := rl.LeaderElectionRecord{
		HolderIdentity:       le.config.Lock.Identity(),
		LeaseDurationSeconds: int(le.config.LeaseDuration / time.Second),
		RenewTime:            now,
		AcquireTime:          now,
	}

	// 1. obtain or create the ElectionRecord
	oldLeaderElectionRecord, err := le.config.Lock.Get()
	if err != nil {
		if !errors.IsNotFound(err) {
			klog.Errorf("error retrieving resource lock %v: %v", le.config.Lock.Describe(), err)
			return false
		}
		if err = le.config.Lock.Create(leaderElectionRecord); err != nil {
			klog.Errorf("error initially creating leader election record: %v", err)
			return false
		}
		le.observedRecord = leaderElectionRecord
		le.observedTime = le.clock.Now()
		return true
	}

	// 2. Record obtained, check the Identity & Time
	if !reflect.DeepEqual(le.observedRecord, *oldLeaderElectionRecord) {
		le.observedRecord = *oldLeaderElectionRecord
		le.observedTime = le.clock.Now()
	}
	if len(oldLeaderElectionRecord.HolderIdentity) > 0 &&
		le.observedTime.Add(le.config.LeaseDuration).After(now.Time) &&
		!le.IsLeader() {
		klog.V(4).Infof("lock is held by %v and has not yet expired", oldLeaderElectionRecord.HolderIdentity)
		return false
	}

	// 3. We're going to try to update. The leaderElectionRecord is set to it's default
	// here. Let's correct it before updating.
	if le.IsLeader() {
		leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime
		leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions
	} else {
		leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1
	}

	// update the lock itself
	if err = le.config.Lock.Update(leaderElectionRecord); err != nil {
		klog.Errorf("Failed to update lock: %v", err)
		return false
	}
	le.observedRecord = leaderElectionRecord
	le.observedTime = le.clock.Now()
	return true
}

func (le *LeaderElector) maybeReportTransition() {
	if le.observedRecord.HolderIdentity == le.reportedLeader {
		return
	}
	le.reportedLeader = le.observedRecord.HolderIdentity
	if le.config.Callbacks.OnNewLeader != nil {
		go le.config.Callbacks.OnNewLeader(le.reportedLeader)
	}
}

// Check will determine if the current lease is expired by more than timeout.
func (le *LeaderElector) Check(maxTolerableExpiredLease time.Duration) error {
	if !le.IsLeader() {
		// Currently not concerned with the case that we are hot standby
		return nil
	}
	// If we are more than timeout seconds after the lease duration that is past the timeout
	// on the lease renew. Time to start reporting ourselves as unhealthy. We should have
	// died but conditions like deadlock can prevent this. (See #70819)
	if le.clock.Since(le.observedTime) > le.config.LeaseDuration+maxTolerableExpiredLease {
		return fmt.Errorf("failed election to renew leadership on lease %s", le.config.Name)
	}

	return nil
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"
	"fmt"
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
)

type textFormatter struct {
}

// Based off logrus.TextFormatter, which behaves completely
// differently when you don't want colored output
func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	b := &bytes.Buffer{}

	levelText := strings.ToUpper(entry.Level.String())[0:4]
	timeStamp := entry.Time.Format("2006/01/02 15:04:05.000000")
	if len(entry.Data) > 0 {
		fmt.Fprintf(b, "%s: %s %-44s ", levelText, timeStamp, entry.Message)
		for k, v := range entry.Data {
			fmt.Fprintf(b, " %s=%v", k, v)
		}
	} else {
		// No padding when there's no fields
		fmt.Fprintf(b, "%s: %s %s", levelText, timeStamp, entry.Message)
	}

	b.WriteByte('\n')
	return b.Bytes(), nil
}

var (
	standardTextFormatter = &textFormatter{}
)

var (
	Log *logrus.Logger
)

func init() {
	Log = &logrus.Logger{
		Out:       os.Stderr,
		Formatter: standardTextFormatter,
		Hooks:     make(logrus.LevelHooks),
	}
}

func SetLogLevel(levelname string) {
	level, err := logrus.ParseLevel(levelname)
	if err != nil {
		Log.Fatal(err)
	}
	Log.Level = level
}
<commit_msg>more idiomatic logger initialisation, with standard default log level<commit_after>package common

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/Sirupsen/logrus"
)

type textFormatter struct {
}

// Based off logrus.TextFormatter, which behaves completely
// differently when you don't want colored output
func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	b := &bytes.Buffer{}

	levelText := strings.ToUpper(entry.Level.String())[0:4]
	timeStamp := entry.Time.Format("2006/01/02 15:04:05.000000")
	if len(entry.Data) > 0 {
		fmt.Fprintf(b, "%s: %s %-44s ", levelText, timeStamp, entry.Message)
		for k, v := range entry.Data {
			fmt.Fprintf(b, " %s=%v", k, v)
		}
	} else {
		// No padding when there's no fields
		fmt.Fprintf(b, "%s: %s %s", levelText, timeStamp, entry.Message)
	}

	b.WriteByte('\n')
	return b.Bytes(), nil
}

var (
	standardTextFormatter = &textFormatter{}
)

var (
	Log *logrus.Logger
)

func init() {
	Log = logrus.New()
	Log.Formatter = standardTextFormatter
}

func SetLogLevel(levelname string) {
	level, err := logrus.ParseLevel(levelname)
	if err != nil {
		Log.Fatal(err)
	}
	Log.Level = level
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package engine

import (
	"io"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
)

type SaltpackEncryptArg struct {
	Opts   keybase1.SaltpackEncryptOptions
	Source io.Reader
	Sink   io.WriteCloser
}

// SaltpackEncrypt encrypts data read from a source into a sink
// for a set of users.  It will track them if necessary.
type SaltpackEncrypt struct {
	arg *SaltpackEncryptArg
	libkb.Contextified
	me *libkb.User
}

// NewSaltpackEncrypt creates a SaltpackEncrypt engine.
func NewSaltpackEncrypt(arg *SaltpackEncryptArg, g *libkb.GlobalContext) *SaltpackEncrypt {
	return &SaltpackEncrypt{
		arg:          arg,
		Contextified: libkb.NewContextified(g),
	}
}

// Name is the unique engine name.
func (e *SaltpackEncrypt) Name() string {
	return "SaltpackEncrypt"
}

// GetPrereqs returns the engine prereqs.
func (e *SaltpackEncrypt) Prereqs() Prereqs {
	return Prereqs{}
}

// RequiredUIs returns the required UIs.
func (e *SaltpackEncrypt) RequiredUIs() []libkb.UIKind {
	return nil
}

// SubConsumers returns the other UI consumers for this engine.
func (e *SaltpackEncrypt) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{
		&DeviceKeyfinder{},
	}
}

func (e *SaltpackEncrypt) loadMyPublicKeys() ([]libkb.NaclDHKeyPublic, error) {

	var ret []libkb.NaclDHKeyPublic

	ckf := e.me.GetComputedKeyFamily()
	if ckf == nil {
		return ret, libkb.NoKeyError{Msg: "no suitable encryption keys found for you"}
	}
	keys := ckf.GetAllActiveSubkeys()
	for _, key := range keys {
		if kp, ok := key.(libkb.NaclDHKeyPair); ok {
			ret = append(ret, kp.Public)
		}
	}

	if len(ret) == 0 {
		return ret, libkb.NoKeyError{Msg: "no suitable encryption keys found for you"}
	}
	return ret, nil
}

func (e *SaltpackEncrypt) loadMe(ctx *Context) error {
	loggedIn, uid, err := IsLoggedIn(e, ctx)
	if err != nil || !loggedIn {
		return err
	}
	e.me, err = libkb.LoadMeByUID(e.G(), uid)
	return err
}

// Run starts the engine.
func (e *SaltpackEncrypt) Run(ctx *Context) (err error) {
	e.G().Log.Debug("+ SaltpackEncrypt::Run")
	defer func() {
		e.G().Log.Debug("- SaltpackEncrypt::Run -> %v", err)
	}()

	var receivers []libkb.NaclDHKeyPublic
	var sender libkb.NaclDHKeyPair

	if err = e.loadMe(ctx); err != nil {
		return err
	}

	if !e.arg.Opts.NoSelfEncrypt && e.me != nil {
		receivers, err = e.loadMyPublicKeys()
		if err != nil {
			return err
		}
	}

	kfarg := DeviceKeyfinderArg{
		Users:           e.arg.Opts.Recipients,
		NeedEncryptKeys: true,
		Self:            e.me,
	}

	kf := NewDeviceKeyfinder(e.G(), kfarg)
	if err := RunEngine(kf, ctx); err != nil {
		return err
	}
	uplus := kf.UsersPlusKeys()
	for _, up := range uplus {
		for _, k := range up.DeviceKeys {
			gk, err := libkb.ImportKeypairFromKID(k.KID)
			if err != nil {
				return err
			}
			kp, ok := gk.(libkb.NaclDHKeyPair)
			if !ok {
				return libkb.KeyCannotEncryptError{}
			}
			receivers = append(receivers, kp.Public)
		}
	}

	if !e.arg.Opts.HideSelf && e.me != nil {
		ska := libkb.SecretKeyArg{
			Me:      e.me,
			KeyType: libkb.DeviceEncryptionKeyType,
		}
		key, err := e.G().Keyrings.GetSecretKeyWithPrompt(ctx.SecretKeyPromptArg(ska, "encrypting a message/file"))
		if err != nil {
			return err
		}
		kp, ok := key.(libkb.NaclDHKeyPair)
		if !ok || kp.Private == nil {
			return libkb.KeyCannotDecryptError{}
		}
		sender = kp
	}

	encarg := libkb.SaltpackEncryptArg{
		Source:         e.arg.Source,
		Sink:           e.arg.Sink,
		Receivers:      receivers,
		Sender:         sender,
		Binary:         e.arg.Opts.Binary,
		HideRecipients: e.arg.Opts.HideRecipients,
	}
	return libkb.SaltpackEncrypt(e.G(), &encarg)
}
<commit_msg>Fix encrypt prereqs<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package engine

import (
	"io"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
)

type SaltpackEncryptArg struct {
	Opts   keybase1.SaltpackEncryptOptions
	Source io.Reader
	Sink   io.WriteCloser
}

// SaltpackEncrypt encrypts data read from a source into a sink
// for a set of users.  It will track them if necessary.
type SaltpackEncrypt struct {
	arg *SaltpackEncryptArg
	libkb.Contextified
	me *libkb.User
}

// NewSaltpackEncrypt creates a SaltpackEncrypt engine.
func NewSaltpackEncrypt(arg *SaltpackEncryptArg, g *libkb.GlobalContext) *SaltpackEncrypt {
	return &SaltpackEncrypt{
		arg:          arg,
		Contextified: libkb.NewContextified(g),
	}
}

// Name is the unique engine name.
func (e *SaltpackEncrypt) Name() string {
	return "SaltpackEncrypt"
}

// GetPrereqs returns the engine prereqs.
func (e *SaltpackEncrypt) Prereqs() Prereqs {
	if e.arg.Opts.NoSelfEncrypt {
		return Prereqs{}
	}
	return Prereqs{Device: true}
}

// RequiredUIs returns the required UIs.
func (e *SaltpackEncrypt) RequiredUIs() []libkb.UIKind {
	return nil
}

// SubConsumers returns the other UI consumers for this engine.
func (e *SaltpackEncrypt) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{
		&DeviceKeyfinder{},
	}
}

func (e *SaltpackEncrypt) loadMyPublicKeys() ([]libkb.NaclDHKeyPublic, error) {

	var ret []libkb.NaclDHKeyPublic

	ckf := e.me.GetComputedKeyFamily()
	if ckf == nil {
		return ret, libkb.NoKeyError{Msg: "no suitable encryption keys found for you"}
	}
	keys := ckf.GetAllActiveSubkeys()
	for _, key := range keys {
		if kp, ok := key.(libkb.NaclDHKeyPair); ok {
			ret = append(ret, kp.Public)
		}
	}

	if len(ret) == 0 {
		return ret, libkb.NoKeyError{Msg: "no suitable encryption keys found for you"}
	}
	return ret, nil
}

func (e *SaltpackEncrypt) loadMe(ctx *Context) error {
	loggedIn, uid, err := IsLoggedIn(e, ctx)
	if err != nil || !loggedIn {
		return err
	}
	e.me, err = libkb.LoadMeByUID(e.G(), uid)
	return err
}

// Run starts the engine.
func (e *SaltpackEncrypt) Run(ctx *Context) (err error) {
	e.G().Log.Debug("+ SaltpackEncrypt::Run")
	defer func() {
		e.G().Log.Debug("- SaltpackEncrypt::Run -> %v", err)
	}()

	var receivers []libkb.NaclDHKeyPublic
	var sender libkb.NaclDHKeyPair

	if err = e.loadMe(ctx); err != nil {
		return err
	}

	if !e.arg.Opts.NoSelfEncrypt && e.me != nil {
		receivers, err = e.loadMyPublicKeys()
		if err != nil {
			return err
		}
	}

	kfarg := DeviceKeyfinderArg{
		Users:           e.arg.Opts.Recipients,
		NeedEncryptKeys: true,
		Self:            e.me,
	}

	kf := NewDeviceKeyfinder(e.G(), kfarg)
	if err := RunEngine(kf, ctx); err != nil {
		return err
	}
	uplus := kf.UsersPlusKeys()
	for _, up := range uplus {
		for _, k := range up.DeviceKeys {
			gk, err := libkb.ImportKeypairFromKID(k.KID)
			if err != nil {
				return err
			}
			kp, ok := gk.(libkb.NaclDHKeyPair)
			if !ok {
				return libkb.KeyCannotEncryptError{}
			}
			receivers = append(receivers, kp.Public)
		}
	}

	if !e.arg.Opts.HideSelf && e.me != nil {
		ska := libkb.SecretKeyArg{
			Me:      e.me,
			KeyType: libkb.DeviceEncryptionKeyType,
		}
		key, err := e.G().Keyrings.GetSecretKeyWithPrompt(ctx.SecretKeyPromptArg(ska, "encrypting a message/file"))
		if err != nil {
			return err
		}
		kp, ok := key.(libkb.NaclDHKeyPair)
		if !ok || kp.Private == nil {
			return libkb.KeyCannotDecryptError{}
		}
		sender = kp
	}

	encarg := libkb.SaltpackEncryptArg{
		Source:         e.arg.Source,
		Sink:           e.arg.Sink,
		Receivers:      receivers,
		Sender:         sender,
		Binary:         e.arg.Opts.Binary,
		HideRecipients: e.arg.Opts.HideRecipients,
	}
	return libkb.SaltpackEncrypt(e.G(), &encarg)
}
<|endoftext|>


<commit_before>// Package rsawrap provides handy wrappers for creating RSA keys and encrypting
// or decrypting messages with RSA-OAEP.
package rsawrap

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"
	"errors"
)

// EncryptOAEP encrypts a plaintext using a PEM encoded RSA public key.
func EncryptOAEP(msg []byte, pubKey []byte) ([]byte, error) {
	hash := sha256.New()
	block, _ := pem.Decode(pubKey)
	if block == nil {
		return nil, errors.New("public key error")
	}
	pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	pub := pubInterface.(*rsa.PublicKey)
	ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, pub, msg, nil)
	if err != nil {
		return nil, err
	}
	return ciphertext, nil
}

// DecryptOAEP decrypts an RSA-OAEP encrypted message. If the password is
// not an empty string, it will try to decrypt a PEM-armored private key.
// Although there are no known attacks against SHA1 in this context, SHA256
// is used as hash algorithm.
func DecryptOAEP(msg []byte, key []byte, passwd string) ([]byte, error) {
	var err error
	hash := sha256.New()
	privateKeyBlock, _ := pem.Decode(key)
	var privKey *rsa.PrivateKey
	var prk []byte

	if passwd != "" {
		pw := []byte(passwd)
		dec := x509.DecryptPEMBlock
		if prk, err = dec(privateKeyBlock, pw); err != nil {
			return nil, err
		}
	} else {
		prk = privateKeyBlock.Bytes
	}

	privKey, err = x509.ParsePKCS1PrivateKey(prk)
	if err != nil {
		return nil, errors.New("private key error. " +
			"Corrupt key or invalid armor password.")
	}
	plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, privKey, msg, nil)
	if err != nil {
		return nil, err
	}
	return plaintext, nil
}

// CreateKey takes the key length in bits and a password as arguments and
// returns an RSA key pair in PEM format. If the password is an empty string, the
// private key PEM will not be encrypted.
func CreateKey(bits int, passwd string) ([]byte, []byte, error) {
	var err error
	rd := rand.Reader
	var key *rsa.PrivateKey
	if key, err = rsa.GenerateKey(rd, bits); err != nil {
		return nil, nil, err
	}

	// Convert the private key to PEM.
	prk := &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	}
	// Encrypt the PEM when passwd is not empty.
	if passwd != "" {
		pw := []byte(passwd)
		enc := x509.EncryptPEMBlock
		cipher := x509.PEMCipherAES256
		if prk, err = enc(rd, prk.Type, prk.Bytes, pw, cipher); err != nil {
			return nil, nil, err
		}
	}
	pemPrivKey := pem.EncodeToMemory(prk)

	// Extract and encode the public key.
	pubKey := key.PublicKey
	var asn1Bytes []byte
	if asn1Bytes, err = x509.MarshalPKIXPublicKey(&pubKey); err != nil {
		return nil, nil, err
	}
	pemKeyBlock := &pem.Block{
		Type:  "RSA PUBLIC KEY",
		Bytes: asn1Bytes,
	}
	pemPubKey := pem.EncodeToMemory(pemKeyBlock)

	return pemPrivKey, pemPubKey, nil
}
<commit_msg>changed passwd to byte slice for consistency<commit_after>// Package rsawrap provides handy wrappers for creating RSA keys and encrypting
// or decrypting messages with RSA-OAEP.
package rsawrap

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"
	"errors"
)

// EncryptOAEP encrypts a plaintext using a PEM encoded RSA public key.
func EncryptOAEP(msg []byte, pubKey []byte) ([]byte, error) {
	hash := sha256.New()
	block, _ := pem.Decode(pubKey)
	if block == nil {
		return nil, errors.New("public key error")
	}
	pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	pub := pubInterface.(*rsa.PublicKey)
	ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, pub, msg, nil)
	if err != nil {
		return nil, err
	}
	return ciphertext, nil
}

// DecryptOAEP decrypts an RSA-OAEP encrypted message. If the password is
// not an empty string, it will try to decrypt a PEM-armored private key.
// Although there are no known attacks against SHA1 in this context, SHA256
// is used as hash algorithm.
func DecryptOAEP(msg []byte, key []byte, passwd []byte) ([]byte, error) {
	var err error
	hash := sha256.New()
	privateKeyBlock, _ := pem.Decode(key)
	var privKey *rsa.PrivateKey
	var prk []byte

	if string(passwd) != "" {
		pw := passwd
		dec := x509.DecryptPEMBlock
		if prk, err = dec(privateKeyBlock, pw); err != nil {
			return nil, err
		}
	} else {
		prk = privateKeyBlock.Bytes
	}

	privKey, err = x509.ParsePKCS1PrivateKey(prk)
	if err != nil {
		return nil, errors.New("private key error. " +
			"Corrupt key or invalid armor password.")
	}
	plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, privKey, msg, nil)
	if err != nil {
		return nil, err
	}
	return plaintext, nil
}

// CreateKey takes the key length in bits and a password as arguments and
// returns an RSA key pair in PEM format. If the password is an empty string, the
// private key PEM will not be encrypted.
func CreateKey(bits int, passwd []byte) ([]byte, []byte, error) {
	var err error
	rd := rand.Reader
	var key *rsa.PrivateKey
	if key, err = rsa.GenerateKey(rd, bits); err != nil {
		return nil, nil, err
	}

	// Convert the private key to PEM.
	prk := &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	}
	// Encrypt the PEM when passwd is not empty.
	if string(passwd) != "" {
		pw := passwd
		enc := x509.EncryptPEMBlock
		cipher := x509.PEMCipherAES256
		if prk, err = enc(rd, prk.Type, prk.Bytes, pw, cipher); err != nil {
			return nil, nil, err
		}
	}
	pemPrivKey := pem.EncodeToMemory(prk)

	// Extract and encode the public key.
	pubKey := key.PublicKey
	var asn1Bytes []byte
	if asn1Bytes, err = x509.MarshalPKIXPublicKey(&pubKey); err != nil {
		return nil, nil, err
	}
	pemKeyBlock := &pem.Block{
		Type:  "RSA PUBLIC KEY",
		Bytes: asn1Bytes,
	}
	pemPubKey := pem.EncodeToMemory(pemKeyBlock)

	return pemPrivKey, pemPubKey, nil
}
<|endoftext|>


<commit_before>package pally

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/model"
	"github.com/uber-go/tally"
	"go.uber.org/atomic"
)

// A Registry is a collection of metrics, usually scoped to a single package or
// object. Each Registry is also its own http.Handler, and can serve
// Prometheus-flavored text and protocol buffer pages for metrics introspection
// or scraping.
type Registry struct {
	metricsMu   sync.RWMutex
	metrics     []metric
	constLabels Labels

	// TODO: To serve our own Prometheus endpoints, we only need to implement
	// prometheus.Gatherer. The vanilla Prometheus registry is a convenient
	// implementation, but it enforces metric uniqueness via FNV64a; if we run
	// into hash collisions in our more-demanding applications, we should use a
	// simple map[string]struct{} instead.
	prom      *prometheus.Registry
	federated []prometheus.Registerer
	handler   http.Handler

	pushing atomic.Bool
	scope   tally.Scope
	ticker  *time.Ticker
	stop    chan struct{}
	stopped chan struct{}
}

// A RegistryOption configures a Registry.
type RegistryOption func(*Registry)

// Federated links a pally.Registry with a prometheus.Registerer, so that all
// metrics created in one also appear in the other.
func Federated(prom prometheus.Registerer) RegistryOption {
	return func(r *Registry) {
		r.federated = append(r.federated, prom)
	}
}

// Labeled adds constant labels to a Registry. All metrics created by a
// Registry inherit its constant labels.
func Labeled(ls Labels) RegistryOption {
	return func(r *Registry) {
		for k, v := range ls {
			if !model.LabelName(k).IsValid() || !model.LabelValue(v).IsValid() {
				continue
			}
			if !isValidTallyString(k) || !isValidTallyString(v) {
				continue
			}
			r.constLabels[k] = v
		}
	}
}

// NewRegistry constructs a new Registry.
func NewRegistry(opts ...RegistryOption) *Registry {
	prom := prometheus.NewRegistry()
	handler := promhttp.HandlerFor(prom, promhttp.HandlerOpts{
		ErrorHandling: promhttp.HTTPErrorOnError, // 500 on errors
	})

	r := &Registry{
		metrics:     make([]metric, 0, _defaultVectorSize),
		constLabels: make(Labels),
		prom:        prom,
		// Assume that we'll be federated with the global prometheus Registry.
		federated: make([]prometheus.Registerer, 0, 1),
		handler:   handler,
		stop:      make(chan struct{}),
		stopped:   make(chan struct{}),
	}
	for _, opt := range opts {
		opt(r)
	}
	return r
}

// Push starts a goroutine that periodically exports all registered metrics to
// a Tally scope. Each Registry can only push to a single Scope; calling Push a
// second time returns an error.
func (r *Registry) Push(scope tally.Scope, tick time.Duration) (context.CancelFunc, error) {
	if r.pushing.Swap(true) {
		return nil, errors.New("already pushing to Tally")
	}
	r.scope = scope
	r.ticker = time.NewTicker(tick)
	go r.exportToTally()
	return func() {
		r.ticker.Stop()
		close(r.stop)
		<-r.stopped
	}, nil
}

// NewCounter constructs a new Counter.
func (r *Registry) NewCounter(opts Opts) (Counter, error) {
	opts = r.addConstLabels(opts)
	if err := opts.validate(); err != nil {
		return nil, err
	}
	c := newCounter(opts)
	if err := r.register(c); err != nil {
		return nil, err
	}
	return c, nil
}

// MustCounter constructs a new Counter. It panics if it encounters an error.
func (r *Registry) MustCounter(opts Opts) Counter {
	c, err := r.NewCounter(opts)
	if err != nil {
		panic(fmt.Sprintf("failed to create Counter with options %+v: %v", opts, err))
	}
	return c
}

// NewGauge constructs a new Gauge.
func (r *Registry) NewGauge(opts Opts) (Gauge, error) {
	opts = r.addConstLabels(opts)
	if err := opts.validate(); err != nil {
		return nil, err
	}
	g := newGauge(opts)
	if err := r.register(g); err != nil {
		return nil, err
	}
	return g, nil
}

// MustGauge constructs a new Gauge. It panics if it encounters an error.
func (r *Registry) MustGauge(opts Opts) Gauge {
	g, err := r.NewGauge(opts)
	if err != nil {
		panic(fmt.Sprintf("failed to create Gauge with options %+v: %v", opts, err))
	}
	return g
}

// NewCounterVector constructs a new CounterVector.
func (r *Registry) NewCounterVector(opts Opts) (CounterVector, error) {
	opts = r.addConstLabels(opts)
	if err := opts.validateVector(); err != nil {
		return nil, err
	}
	v := newCounterVector(opts)
	if err := r.register(v); err != nil {
		return nil, err
	}
	return v, nil
}

// MustCounterVector constructs a new CounterVector. It panics if it encounters
// an error.
func (r *Registry) MustCounterVector(opts Opts) CounterVector {
	v, err := r.NewCounterVector(opts)
	if err != nil {
		panic(fmt.Sprintf("failed to create CounterVector with options %+v: %v", opts, err))
	}
	return v
}

// NewGaugeVector constructs a new CounterVector.
func (r *Registry) NewGaugeVector(opts Opts) (GaugeVector, error) {
	opts = r.addConstLabels(opts)
	if err := opts.validateVector(); err != nil {
		return nil, err
	}
	v := newGaugeVector(opts)
	if err := r.register(v); err != nil {
		return nil, err
	}
	return v, nil
}

// MustGaugeVector constructs a new GaugeVector. It panics if it encounters an
// error.
func (r *Registry) MustGaugeVector(opts Opts) GaugeVector {
	v, err := r.NewGaugeVector(opts)
	if err != nil {
		panic(fmt.Sprintf("failed to create GaugeVector with options %+v: %v", opts, err))
	}
	return v
}

// ServeHTTP implements http.Handler.
func (r *Registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.handler.ServeHTTP(w, req)
}

func (r *Registry) exportToTally() {
	defer close(r.stopped)
	// When stopping, do one last export to catch any stragglers.
	defer r.push()

	for {
		select {
		case <-r.stop:
			return
		case <-r.ticker.C:
			r.push()
		}
	}
}

func (r *Registry) push() {
	r.metricsMu.RLock()
	for _, m := range r.metrics {
		m.push(r.scope)
	}
	r.metricsMu.RUnlock()
}

func (r *Registry) register(m metric) error {
	r.metricsMu.Lock()
	r.metrics = append(r.metrics, m)
	r.metricsMu.Unlock()

	if err := r.prom.Register(m); err != nil {
		return err
	}
	for _, fed := range r.federated {
		if err := fed.Register(m); err != nil {
			return err
		}
	}
	return nil
}

func (r *Registry) addConstLabels(opts Opts) Opts {
	if len(r.constLabels) == 0 {
		return opts
	}
	labels := opts.copyLabels()
	for k, v := range r.constLabels {
		labels[k] = v
	}
	opts.ConstLabels = labels
	return opts
}
<commit_msg>Separate pusher and registry<commit_after>package pally

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/model"
	"github.com/uber-go/tally"
	"go.uber.org/atomic"
)

// A Registry is a collection of metrics, usually scoped to a single package or
// object. Each Registry is also its own http.Handler, and can serve
// Prometheus-flavored text and protocol buffer pages for metrics introspection
// or scraping.
type Registry struct {
	metricsMu   sync.RWMutex
	metrics     []metric
	constLabels Labels

	// TODO: To serve our own Prometheus endpoints, we only need to implement
	// prometheus.Gatherer. The vanilla Prometheus registry is a convenient
	// implementation, but it enforces metric uniqueness via FNV64a; if we run
	// into hash collisions in our more-demanding applications, we should use a
	// simple map[string]struct{} instead.
	prom      *prometheus.Registry
	federated []prometheus.Registerer
	handler   http.Handler

	// Registries can only push to a single Tally scope. Since Tally scopes
	// support tee'ing to multiple backends, this isn't a problem in practice.
	pushing atomic.Bool
}

// A RegistryOption configures a Registry.
type RegistryOption func(*Registry)

// Federated links a pally.Registry with a prometheus.Registerer, so that all
// metrics created in one also appear in the other.
func Federated(prom prometheus.Registerer) RegistryOption {
	return func(r *Registry) {
		r.federated = append(r.federated, prom)
	}
}

// Labeled adds constant labels to a Registry. All metrics created by a
// Registry inherit its constant labels.
func Labeled(ls Labels) RegistryOption {
	return func(r *Registry) {
		for k, v := range ls {
			if !model.LabelName(k).IsValid() || !model.LabelValue(v).IsValid() {
				continue
			}
			if !isValidTallyString(k) || !isValidTallyString(v) {
				continue
			}
			r.constLabels[k] = v
		}
	}
}

// NewRegistry constructs a new Registry.
func NewRegistry(opts ...RegistryOption) *Registry {
	prom := prometheus.NewRegistry()
	handler := promhttp.HandlerFor(prom, promhttp.HandlerOpts{
		ErrorHandling: promhttp.HTTPErrorOnError, // 500 on errors
	})

	r := &Registry{
		metrics:     make([]metric, 0, _defaultVectorSize),
		constLabels: make(Labels),
		prom:        prom,
		// Assume that we'll be federated with the global prometheus Registry.
		federated: make([]prometheus.Registerer, 0, 1),
		handler:   handler,
	}
	for _, opt := range opts {
		opt(r)
	}
	return r
}

// Push starts a goroutine that periodically exports all registered metrics to
// a Tally scope. Each Registry can only push to a single Scope; calling Push a
// second time returns an error.
//
// In practice, this isn't a problem because Tally scopes natively support
// tee'ing to multiple backends.
func (r *Registry) Push(scope tally.Scope, tick time.Duration) (context.CancelFunc, error) {
	if r.pushing.Swap(true) {
		return nil, errors.New("already pushing to Tally")
	}
	pusher := newPusher(r, scope, tick)
	go pusher.Start()
	return pusher.Stop, nil
}

// NewCounter constructs a new Counter.
func (r *Registry) NewCounter(opts Opts) (Counter, error) {
	opts = r.addConstLabels(opts)
	if err := opts.validate(); err != nil {
		return nil, err
	}
	c := newCounter(opts)
	if err := r.register(c); err != nil {
		return nil, err
	}
	return c, nil
}

// MustCounter constructs a new Counter. It panics if it encounters an error.
func (r *Registry) MustCounter(opts Opts) Counter {
	c, err := r.NewCounter(opts)
	if err != nil {
		panic(fmt.Sprintf("failed to create Counter with options %+v: %v", opts, err))
	}
	return c
}

// NewGauge constructs a new Gauge.
func (r *Registry) NewGauge(opts Opts) (Gauge, error) {
	opts = r.addConstLabels(opts)
	if err := opts.validate(); err != nil {
		return nil, err
	}
	g := newGauge(opts)
	if err := r.register(g); err != nil {
		return nil, err
	}
	return g, nil
}

// MustGauge constructs a new Gauge. It panics if it encounters an error.
func (r *Registry) MustGauge(opts Opts) Gauge {
	g, err := r.NewGauge(opts)
	if err != nil {
		panic(fmt.Sprintf("failed to create Gauge with options %+v: %v", opts, err))
	}
	return g
}

// NewCounterVector constructs a new CounterVector.
func (r *Registry) NewCounterVector(opts Opts) (CounterVector, error) {
	opts = r.addConstLabels(opts)
	if err := opts.validateVector(); err != nil {
		return nil, err
	}
	v := newCounterVector(opts)
	if err := r.register(v); err != nil {
		return nil, err
	}
	return v, nil
}

// MustCounterVector constructs a new CounterVector. It panics if it encounters
// an error.
func (r *Registry) MustCounterVector(opts Opts) CounterVector {
	v, err := r.NewCounterVector(opts)
	if err != nil {
		panic(fmt.Sprintf("failed to create CounterVector with options %+v: %v", opts, err))
	}
	return v
}

// NewGaugeVector constructs a new CounterVector.
func (r *Registry) NewGaugeVector(opts Opts) (GaugeVector, error) {
	opts = r.addConstLabels(opts)
	if err := opts.validateVector(); err != nil {
		return nil, err
	}
	v := newGaugeVector(opts)
	if err := r.register(v); err != nil {
		return nil, err
	}
	return v, nil
}

// MustGaugeVector constructs a new GaugeVector. It panics if it encounters an
// error.
func (r *Registry) MustGaugeVector(opts Opts) GaugeVector {
	v, err := r.NewGaugeVector(opts)
	if err != nil {
		panic(fmt.Sprintf("failed to create GaugeVector with options %+v: %v", opts, err))
	}
	return v
}

// ServeHTTP implements http.Handler.
func (r *Registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.handler.ServeHTTP(w, req)
}

func (r *Registry) register(m metric) error {
	r.metricsMu.Lock()
	r.metrics = append(r.metrics, m)
	r.metricsMu.Unlock()

	if err := r.prom.Register(m); err != nil {
		return err
	}
	for _, fed := range r.federated {
		if err := fed.Register(m); err != nil {
			return err
		}
	}
	return nil
}

func (r *Registry) addConstLabels(opts Opts) Opts {
	if len(r.constLabels) == 0 {
		return opts
	}
	labels := opts.copyLabels()
	for k, v := range r.constLabels {
		labels[k] = v
	}
	opts.ConstLabels = labels
	return opts
}

type pusher struct {
	reg     *Registry
	stop    chan struct{}
	stopped chan struct{}
	scope   tally.Scope
	ticker  *time.Ticker
}

func newPusher(r *Registry, scope tally.Scope, tick time.Duration) *pusher {
	return &pusher{
		reg:     r,
		stop:    make(chan struct{}),
		stopped: make(chan struct{}),
		scope:   scope,
		ticker:  time.NewTicker(tick),
	}
}

func (p *pusher) Start() {
	defer close(p.stopped)
	// When stopping, do one last export to catch any stragglers.
	defer p.push()

	for {
		select {
		case <-p.stop:
			return
		case <-p.ticker.C:
			p.push()
		}
	}
}

func (p *pusher) Stop() {
	p.ticker.Stop()
	close(p.stop)
	<-p.stopped
}

func (p *pusher) push() {
	p.reg.metricsMu.RLock()
	for _, m := range p.reg.metrics {
		m.push(p.scope)
	}
	p.reg.metricsMu.RUnlock()
}
<|endoftext|>


<commit_before>package logg

import (
	"fmt"
	"log"
)

const (
	// DEBUG -- debug log level
	DEBUG = iota
	// INFO -- info log level
	INFO = iota
	// WARNING -- warning log level
	WARNING = iota
	// ERROR -- error log level
	ERROR = iota
	// FATAL -- fatal log level
	FATAL = iota
)

var _level = INFO
var _levels = map[int]string{}

func init() {
	// Debug, Info, Warning, Error, Fatal
	_levels[DEBUG] = "Debug"
	_levels[INFO] = "Info"
	_levels[WARNING] = "Warning"
	_levels[ERROR] = "Error"
	_levels[FATAL] = "Fatal"
}

// Log -- log message with the given log level
func Log(level int, args ...interface{}) {
	prefix := fmt.Sprintf("%s: ", _getName(level))
	args = append([]interface{}{prefix}, args...)
	if level == FATAL {
		log.Fatal(args...)
	}
	if level < _level {
		return
	}

	log.Print(args...)
}

// Logf -- formatted log message with the given log level
func Logf(level int, format string, args ...interface{}) {
	format = fmt.Sprintf("%s: %s", _getName(level), format)

	if level == FATAL {
		log.Fatalf(format, args...)
	}
	if level < _level {
		return
	}

	log.Printf(format, args...)
}

// Fatal -- fail with the given error message
func Fatal(args ...interface{}) {
	Log(FATAL, args...)
}

// Fatalf -- fail with the given error message
func Fatalf(fmt string, args ...interface{}) {
	Logf(FATAL, fmt, args...)
}

// Error -- log error message
func Error(args ...interface{}) {
	Log(ERROR, args...)
}

// Errorf -- log formatted error message
func Errorf(fmt string, args ...interface{}) {
	Logf(ERROR, fmt, args...)
}

// Warning -- log warning message
func Warning(args ...interface{}) {
	Log(WARNING, args...)
}

// Warningf -- log formatted warning message
func Warningf(fmt string, args ...interface{}) {
	Logf(WARNING, fmt, args...)
}

// Info -- log info message
func Info(args ...interface{}) {
	Log(INFO, args...)
}

// Infof -- log formatted info message
func Infof(fmt string, args ...interface{}) {
	Logf(INFO, fmt, args...)
}

// Debug -- log debug message
func Debug(args ...interface{}) {
	Log(DEBUG, args...)
}

// Debugf -- log formatted debug message
func Debugf(fmt string, args ...interface{}) {
	Logf(DEBUG, fmt, args...)
}

func _getName(level int) string {
	rc, _ := _levels[level]
	return rc
}
<commit_msg>logg: added SetLevel()<commit_after>package logg

import (
	"fmt"
	"log"
)

const (
	// DEBUG -- debug log level
	DEBUG = iota
	// INFO -- info log level
	INFO = iota
	// WARNING -- warning log level
	WARNING = iota
	// ERROR -- error log level
	ERROR = iota
	// FATAL -- fatal log level
	FATAL = iota
)

var _level = INFO
var _levels = map[int]string{}

func init() {
	// Debug, Info, Warning, Error, Fatal
	_levels[DEBUG] = "Debug"
	_levels[INFO] = "Info"
	_levels[WARNING] = "Warning"
	_levels[ERROR] = "Error"
	_levels[FATAL] = "Fatal"
}

// Log -- log message with the given log level
func Log(level int, args ...interface{}) {
	prefix := fmt.Sprintf("%s: ", _getName(level))
	args = append([]interface{}{prefix}, args...)
	if level == FATAL {
		log.Fatal(args...)
	}
	if level < _level {
		return
	}

	log.Print(args...)
}

// Logf -- formatted log message with the given log level
func Logf(level int, format string, args ...interface{}) {
	format = fmt.Sprintf("%s: %s", _getName(level), format)

	if level == FATAL {
		log.Fatalf(format, args...)
	}
	if level < _level {
		return
	}

	log.Printf(format, args...)
}

// Fatal -- fail with the given error message
func Fatal(args ...interface{}) {
	Log(FATAL, args...)
}

// Fatalf -- fail with the given error message
func Fatalf(fmt string, args ...interface{}) {
	Logf(FATAL, fmt, args...)
}

// Error -- log error message
func Error(args ...interface{}) {
	Log(ERROR, args...)
}

// Errorf -- log formatted error message
func Errorf(fmt string, args ...interface{}) {
	Logf(ERROR, fmt, args...)
}

// Warning -- log warning message
func Warning(args ...interface{}) {
	Log(WARNING, args...)
}

// Warningf -- log formatted warning message
func Warningf(fmt string, args ...interface{}) {
	Logf(WARNING, fmt, args...)
}

// Info -- log info message
func Info(args ...interface{}) {
	Log(INFO, args...)
}

// Infof -- log formatted info message
func Infof(fmt string, args ...interface{}) {
	Logf(INFO, fmt, args...)
}

// Debug -- log debug message
func Debug(args ...interface{}) {
	Log(DEBUG, args...)
}

// Debugf -- log formatted debug message
func Debugf(fmt string, args ...interface{}) {
	Logf(DEBUG, fmt, args...)
}

// SetLevel -- Set the minimum log level
func SetLevel(level int) {
	_level = level
}

func _getName(level int) string {
	rc, _ := _levels[level]
	return rc
}
<|endoftext|>


<commit_before>package alias

import "bytes"
import "os/exec"
import "regexp"
import "strconv"
import "strings"
import "io"

import "../interpreter"

type Callee interface {
	String() string
	Call(cmd *exec.Cmd) (interpreter.NextT, error)
}

type AliasFunc struct {
	BaseStr string
}

func New(baseStr string) *AliasFunc {
	this := new(AliasFunc)
	this.BaseStr = baseStr
	return this
}

func (this *AliasFunc) String() string {
	return this.BaseStr
}

func (this *AliasFunc) Call(cmd *exec.Cmd) (interpreter.NextT, error) {
	isReplaced := false
	cmdline := paramMatch.ReplaceAllStringFunc(this.BaseStr, func(s string) string {
		if s == "$*" {
			isReplaced = true
			return quoteAndJoin(cmd.Args[1:])
		}
		i, err := strconv.ParseInt(s[1:], 10, 0)
		if err == nil {
			isReplaced = true
			if 0 <= i && int(i) < len(cmd.Args) {
				return cmd.Args[i]
			}
		}
		return s
	})

	if !isReplaced {
		var buffer bytes.Buffer
		buffer.WriteString(this.BaseStr)
		buffer.WriteRune(' ')
		buffer.WriteString(quoteAndJoin(cmd.Args[1:]))
		cmdline = buffer.String()
	}
	var stdio interpreter.Stdio
	stdio.Stdin = cmd.Stdin
	stdio.Stdout = cmd.Stdout
	stdio.Stderr = cmd.Stderr
	nextT, err := interpreter.Interpret(
		cmdline,
		NextHook, /* commands.Exec */
		&stdio)
	return nextT, err
}

var Table = map[string]Callee{}
var paramMatch = regexp.MustCompile("\\$(\\*|[0-9]+)")

func quoteAndJoin(list []string) string {
	var buffer bytes.Buffer
	for _, value := range list {
		if buffer.Len() > 0 {
			buffer.WriteRune(' ')
		}
		buffer.WriteRune('"')
		buffer.WriteString(value)
		buffer.WriteRune('"')
	}
	return buffer.String()
}

var NextHook func(cmd *exec.Cmd, IsBackground bool, closer io.Closer) (interpreter.NextT, error)

func Hook(cmd *exec.Cmd, IsBackground bool, closer io.Closer) (interpreter.NextT, error) {
	callee, ok := Table[strings.ToLower(cmd.Args[0])]
	if !ok {
		return NextHook(cmd, IsBackground, closer)
	}
	nextT, err := callee.Call(cmd)
	if nextT != interpreter.THROUGH && closer != nil {
		closer.Close()
	}
	return nextT, err
}
<commit_msg>Renmae inteface: Callee -> Callable<commit_after>package alias

import "bytes"
import "os/exec"
import "regexp"
import "strconv"
import "strings"
import "io"

import "../interpreter"

type Callable interface {
	String() string
	Call(cmd *exec.Cmd) (interpreter.NextT, error)
}

type AliasFunc struct {
	BaseStr string
}

func New(baseStr string) *AliasFunc {
	this := new(AliasFunc)
	this.BaseStr = baseStr
	return this
}

func (this *AliasFunc) String() string {
	return this.BaseStr
}

func (this *AliasFunc) Call(cmd *exec.Cmd) (interpreter.NextT, error) {
	isReplaced := false
	cmdline := paramMatch.ReplaceAllStringFunc(this.BaseStr, func(s string) string {
		if s == "$*" {
			isReplaced = true
			return quoteAndJoin(cmd.Args[1:])
		}
		i, err := strconv.ParseInt(s[1:], 10, 0)
		if err == nil {
			isReplaced = true
			if 0 <= i && int(i) < len(cmd.Args) {
				return cmd.Args[i]
			}
		}
		return s
	})

	if !isReplaced {
		var buffer bytes.Buffer
		buffer.WriteString(this.BaseStr)
		buffer.WriteRune(' ')
		buffer.WriteString(quoteAndJoin(cmd.Args[1:]))
		cmdline = buffer.String()
	}
	var stdio interpreter.Stdio
	stdio.Stdin = cmd.Stdin
	stdio.Stdout = cmd.Stdout
	stdio.Stderr = cmd.Stderr
	nextT, err := interpreter.Interpret(
		cmdline,
		NextHook, /* commands.Exec */
		&stdio)
	return nextT, err
}

var Table = map[string]Callable{}
var paramMatch = regexp.MustCompile("\\$(\\*|[0-9]+)")

func quoteAndJoin(list []string) string {
	var buffer bytes.Buffer
	for _, value := range list {
		if buffer.Len() > 0 {
			buffer.WriteRune(' ')
		}
		buffer.WriteRune('"')
		buffer.WriteString(value)
		buffer.WriteRune('"')
	}
	return buffer.String()
}

var NextHook func(cmd *exec.Cmd, IsBackground bool, closer io.Closer) (interpreter.NextT, error)

func Hook(cmd *exec.Cmd, IsBackground bool, closer io.Closer) (interpreter.NextT, error) {
	callee, ok := Table[strings.ToLower(cmd.Args[0])]
	if !ok {
		return NextHook(cmd, IsBackground, closer)
	}
	nextT, err := callee.Call(cmd)
	if nextT != interpreter.THROUGH && closer != nil {
		closer.Close()
	}
	return nextT, err
}
<|endoftext|>


<commit_before>package filemonitor

import (
	"io"
	"os"
	"os/exec"
	"path"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	slog "github.com/burke/zeus/go/shinylog"
)

// {{{ public API
func Start(done chan bool) (filesChanged chan string, quit chan bool) {
	quit = make(chan bool)
	filesChanged = make(chan string, 1024)
	go start(filesChanged, done, quit)
	return
}

func AddFile(file string) {
	filesToWatch <- file
}

// }}}

var filesToWatch chan string

var watcherIn io.WriteCloser
var watcherOut io.ReadCloser
var watcherErr io.ReadCloser

var fileMutex sync.Mutex

var allWatchedFiles map[string]bool

func start(filesChanged chan string, done, quit chan bool) {
	// this is large because as long as we start
	// watching the files eventually, it's more of a priority to
	// get the slaves booted as quickly as possible.
	filesToWatch = make(chan string, 8192)
	allWatchedFiles = make(map[string]bool)

	cmd := startWrapper(filesChanged)

	for {
		select {
		case <-quit:
			cmd.Process.Kill()
			done <- true
			return
		case path := <-filesToWatch:
			go handleLoadedFileNotification(path)
		}
	}
}

func executablePath() string {
	switch runtime.GOOS {
	case "darwin":
		return path.Join(path.Dir(os.Args[0]), "fsevents-wrapper")
	case "linux":
		gemRoot := path.Dir(path.Dir(os.Args[0]))
		return path.Join(gemRoot, "ext/inotify-wrapper/inotify-wrapper")
	}
	terminate("Unsupported OS")
	return ""
}

func startWrapper(filesChanged chan string) *exec.Cmd {
	cmd := exec.Command(executablePath())
	var err error
	if watcherIn, err = cmd.StdinPipe(); err != nil {
		terminate(err.Error())
	}
	if watcherOut, err = cmd.StdoutPipe(); err != nil {
		terminate(err.Error())
	}
	if watcherErr, err = cmd.StderrPipe(); err != nil {
		terminate(err.Error())
	}

	cmd.Start()

	go func() {
		buf := make([]byte, 2048)
		for {
			n, err := watcherOut.Read(buf)
			if err != nil {
				time.Sleep(500 * time.Millisecond)
				slog.Red("Failed to read from FileSystem watcher process: " + err.Error())
			}
			message := strings.TrimSpace(string(buf[:n]))
			files := strings.Split(message, "\n")
			for _, file := range files {
				filesChanged <- file
			}
		}
	}()

	go func() {
		err := cmd.Wait()
		// gross, but this is an easy way to work around the case where
		// signal propagation hits the wrapper before the master disables logging
		time.Sleep(100 * time.Millisecond)
		terminate("The FS watcher process crashed: " + err.Error())
	}()

	return cmd
}

func handleLoadedFileNotification(file string) {
	fileMutex.Lock()
	// a slave loaded a file. It's up to us here to make sure this file is watched.
	if !allWatchedFiles[file] {
		allWatchedFiles[file] = true
		startWatchingFile(file)
	}
	fileMutex.Unlock()
}

func startWatchingFile(file string) {
	_, err := watcherIn.Write([]byte(file + "\n"))
	if err != nil {
		slog.Error(err)
	}
}

func terminate(message string) {
	slog.Red(message)
	println(message)
	proc, _ := os.FindProcess(os.Getpid())
	proc.Signal(syscall.SIGTERM)
}
<commit_msg>enable the new listener when an environment variable is set<commit_after>package filemonitor

import (
	"io"
	"os"
	"os/exec"
	"path"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	slog "github.com/burke/zeus/go/shinylog"
)

// {{{ public API
func Start(done chan bool) (filesChanged chan string, quit chan bool) {
	quit = make(chan bool)
	filesChanged = make(chan string, 1024)
	go start(filesChanged, done, quit)
	return
}

func AddFile(file string) {
	filesToWatch <- file
}

// }}}

var filesToWatch chan string

var watcherIn io.WriteCloser
var watcherOut io.ReadCloser
var watcherErr io.ReadCloser

var fileMutex sync.Mutex

var allWatchedFiles map[string]bool

func start(filesChanged chan string, done, quit chan bool) {
	// this is large because as long as we start
	// watching the files eventually, it's more of a priority to
	// get the slaves booted as quickly as possible.
	filesToWatch = make(chan string, 8192)
	allWatchedFiles = make(map[string]bool)

	cmd := startWrapper(filesChanged)

	for {
		select {
		case <-quit:
			cmd.Process.Kill()
			done <- true
			return
		case path := <-filesToWatch:
			go handleLoadedFileNotification(path)
		}
	}
}

func executablePath() []string {
	port := os.Getenv("ZEUS_NETWORK_FILE_MONITOR_PORT")
	if len(port) > 0 {
		gemRoot := path.Dir(path.Dir(os.Args[0]))
		return []string{path.Join(gemRoot, "ext/file-listener/file-listener"), port}
	} else {
		switch runtime.GOOS {
		case "darwin":
			return []string{path.Join(path.Dir(os.Args[0]), "fsevents-wrapper")}
		case "linux":
			gemRoot := path.Dir(path.Dir(os.Args[0]))
			return []string{path.Join(gemRoot, "ext/inotify-wrapper/inotify-wrapper")}
		}
	}
	terminate("Unsupported OS")
	return []string{}
}

func startWrapper(filesChanged chan string) *exec.Cmd {
	path := executablePath()
	cmd := exec.Command(path[0], path[1:]...)
	var err error
	if watcherIn, err = cmd.StdinPipe(); err != nil {
		terminate(err.Error())
	}
	if watcherOut, err = cmd.StdoutPipe(); err != nil {
		terminate(err.Error())
	}
	if watcherErr, err = cmd.StderrPipe(); err != nil {
		terminate(err.Error())
	}

	cmd.Start()

	go func() {
		buf := make([]byte, 2048)
		for {
			n, err := watcherOut.Read(buf)
			if err != nil {
				time.Sleep(500 * time.Millisecond)
				slog.Red("Failed to read from FileSystem watcher process: " + err.Error())
			}
			message := strings.TrimSpace(string(buf[:n]))
			files := strings.Split(message, "\n")
			for _, file := range files {
				filesChanged <- file
			}
		}
	}()

	go func() {
		err := cmd.Wait()
		// gross, but this is an easy way to work around the case where
		// signal propagation hits the wrapper before the master disables logging
		time.Sleep(100 * time.Millisecond)
		terminate("The FS watcher process crashed: " + err.Error())
	}()

	return cmd
}

func handleLoadedFileNotification(file string) {
	fileMutex.Lock()
	// a slave loaded a file. It's up to us here to make sure this file is watched.
	if !allWatchedFiles[file] {
		allWatchedFiles[file] = true
		startWatchingFile(file)
	}
	fileMutex.Unlock()
}

func startWatchingFile(file string) {
	_, err := watcherIn.Write([]byte(file + "\n"))
	if err != nil {
		slog.Error(err)
	}
}

func terminate(message string) {
	slog.Red(message)
	println(message)
	proc, _ := os.FindProcess(os.Getpid())
	proc.Signal(syscall.SIGTERM)
}
<|endoftext|>


<commit_before>// Copyright 2017 Andrew Morgan <andrew@amorgan.xyz>
//
// 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 config

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"strings"

	log "github.com/sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"
)

type AppServiceAPI struct {
	Matrix  *Global  `yaml:"-"`
	Derived *Derived `yaml:"-"` // TODO: Nuke Derived from orbit

	InternalAPI InternalAPIOptions `yaml:"internal_api"`

	Database DatabaseOptions `yaml:"database"`

	// DisableTLSValidation disables the validation of X.509 TLS certs
	// on appservice endpoints. This is not recommended in production!
	DisableTLSValidation bool `yaml:"disable_tls_validation"`

	ConfigFiles []string `yaml:"config_files"`
}

func (c *AppServiceAPI) Defaults() {
	c.InternalAPI.Listen = "http://localhost:7777"
	c.InternalAPI.Connect = "http://localhost:7777"
	c.Database.Defaults()
	c.Database.ConnectionString = "file:appservice.db"
}

func (c *AppServiceAPI) Verify(configErrs *ConfigErrors, isMonolith bool) {
	checkURL(configErrs, "app_service_api.internal_api.listen", string(c.InternalAPI.Listen))
	checkURL(configErrs, "app_service_api.internal_api.bind", string(c.InternalAPI.Connect))
	checkNotEmpty(configErrs, "app_service_api.database.connection_string", string(c.Database.ConnectionString))
}

// ApplicationServiceNamespace is the namespace that a specific application
// service has management over.
type ApplicationServiceNamespace struct {
	// Whether or not the namespace is managed solely by this application service
	Exclusive bool `yaml:"exclusive"`
	// A regex pattern that represents the namespace
	Regex string `yaml:"regex"`
	// The ID of an existing group that all users of this application service will
	// be added to. This field is only relevant to the `users` namespace.
	// Note that users who are joined to this group through an application service
	// are not to be listed when querying for the group's members, however the
	// group should be listed when querying an application service user's groups.
	// This is to prevent making spamming all users of an application service
	// trivial.
	GroupID string `yaml:"group_id"`
	// Regex object representing our pattern. Saves having to recompile every time
	RegexpObject *regexp.Regexp
}

// ApplicationService represents a Matrix application service.
// https://matrix.org/docs/spec/application_service/unstable.html
type ApplicationService struct {
	// User-defined, unique, persistent ID of the application service
	ID string `yaml:"id"`
	// Base URL of the application service
	URL string `yaml:"url"`
	// Application service token provided in requests to a homeserver
	ASToken string `yaml:"as_token"`
	// Homeserver token provided in requests to an application service
	HSToken string `yaml:"hs_token"`
	// Localpart of application service user
	SenderLocalpart string `yaml:"sender_localpart"`
	// Information about an application service's namespaces. Key is either
	// "users", "aliases" or "rooms"
	NamespaceMap map[string][]ApplicationServiceNamespace `yaml:"namespaces"`
	// Whether rate limiting is applied to each application service user
	RateLimited bool `yaml:"rate_limited"`
	// Any custom protocols that this application service provides (e.g. IRC)
	Protocols []string `yaml:"protocols"`
}

// IsInterestedInRoomID returns a bool on whether an application service's
// namespace includes the given room ID
func (a *ApplicationService) IsInterestedInRoomID(
	roomID string,
) bool {
	if namespaceSlice, ok := a.NamespaceMap["rooms"]; ok {
		for _, namespace := range namespaceSlice {
			if namespace.RegexpObject.MatchString(roomID) {
				return true
			}
		}
	}

	return false
}

// IsInterestedInUserID returns a bool on whether an application service's
// namespace includes the given user ID
func (a *ApplicationService) IsInterestedInUserID(
	userID string,
) bool {
	if namespaceSlice, ok := a.NamespaceMap["users"]; ok {
		for _, namespace := range namespaceSlice {
			if namespace.RegexpObject.MatchString(userID) {
				return true
			}
		}
	}

	return false
}

// OwnsNamespaceCoveringUserId returns a bool on whether an application service's
// namespace is exclusive and includes the given user ID
func (a *ApplicationService) OwnsNamespaceCoveringUserId(
	userID string,
) bool {
	if namespaceSlice, ok := a.NamespaceMap["users"]; ok {
		for _, namespace := range namespaceSlice {
			if namespace.Exclusive && namespace.RegexpObject.MatchString(userID) {
				return true
			}
		}
	}

	return false
}

// IsInterestedInRoomAlias returns a bool on whether an application service's
// namespace includes the given room alias
func (a *ApplicationService) IsInterestedInRoomAlias(
	roomAlias string,
) bool {
	if namespaceSlice, ok := a.NamespaceMap["aliases"]; ok {
		for _, namespace := range namespaceSlice {
			if namespace.RegexpObject.MatchString(roomAlias) {
				return true
			}
		}
	}

	return false
}

// loadAppServices iterates through all application service config files
// and loads their data into the config object for later access.
func loadAppServices(config *AppServiceAPI, derived *Derived) error {
	for _, configPath := range config.ConfigFiles {
		// Create a new application service with default options
		appservice := ApplicationService{
			RateLimited: true,
		}

		// Create an absolute path from a potentially relative path
		absPath, err := filepath.Abs(configPath)
		if err != nil {
			return err
		}

		// Read the application service's config file
		configData, err := ioutil.ReadFile(absPath)
		if err != nil {
			return err
		}

		// Load the config data into our struct
		if err = yaml.UnmarshalStrict(configData, &appservice); err != nil {
			return err
		}

		// Append the parsed application service to the global config
		derived.ApplicationServices = append(
			derived.ApplicationServices, appservice,
		)
	}

	// Check for any errors in the loaded application services
	return checkErrors(config, derived)
}

// setupRegexps will create regex objects for exclusive and non-exclusive
// usernames, aliases and rooms of all application services, so that other
// methods can quickly check if a particular string matches any of them.
func setupRegexps(_ *AppServiceAPI, derived *Derived) (err error) {
	// Combine all exclusive namespaces for later string checking
	var exclusiveUsernameStrings, exclusiveAliasStrings []string

	// If an application service's regex is marked as exclusive, add
	// its contents to the overall exlusive regex string. Room regex
	// not necessary as we aren't denying exclusive room ID creation
	for _, appservice := range derived.ApplicationServices {
		for key, namespaceSlice := range appservice.NamespaceMap {
			switch key {
			case "users":
				appendExclusiveNamespaceRegexs(&exclusiveUsernameStrings, namespaceSlice)
			case "aliases":
				appendExclusiveNamespaceRegexs(&exclusiveAliasStrings, namespaceSlice)
			}
		}
	}

	// Join the regexes together into one big regex.
	// i.e. "app1.*", "app2.*" -> "(app1.*)|(app2.*)"
	// Later we can check if a username or alias matches any exclusive regex and
	// deny access if it isn't from an application service
	exclusiveUsernames := strings.Join(exclusiveUsernameStrings, "|")
	exclusiveAliases := strings.Join(exclusiveAliasStrings, "|")

	// If there are no exclusive regexes, compile string so that it will not match
	// any valid usernames/aliases/roomIDs
	if exclusiveUsernames == "" {
		exclusiveUsernames = "^$"
	}
	if exclusiveAliases == "" {
		exclusiveAliases = "^$"
	}

	// Store compiled Regex
	if derived.ExclusiveApplicationServicesUsernameRegexp, err = regexp.Compile(exclusiveUsernames); err != nil {
		return err
	}
	if derived.ExclusiveApplicationServicesAliasRegexp, err = regexp.Compile(exclusiveAliases); err != nil {
		return err
	}

	return nil
}

// appendExclusiveNamespaceRegexs takes a slice of strings and a slice of
// namespaces and will append the regexes of only the exclusive namespaces
// into the string slice
func appendExclusiveNamespaceRegexs(
	exclusiveStrings *[]string, namespaces []ApplicationServiceNamespace,
) {
	for index, namespace := range namespaces {
		if namespace.Exclusive {
			// We append parenthesis to later separate each regex when we compile
			// i.e. "app1.*", "app2.*" -> "(app1.*)|(app2.*)"
			*exclusiveStrings = append(*exclusiveStrings, "("+namespace.Regex+")")
		}

		// Compile this regex into a Regexp object for later use
		namespaces[index].RegexpObject, _ = regexp.Compile(namespace.Regex)
	}
}

// checkErrors checks for any configuration errors amongst the loaded
// application services according to the application service spec.
func checkErrors(config *AppServiceAPI, derived *Derived) (err error) {
	var idMap = make(map[string]bool)
	var tokenMap = make(map[string]bool)

	// Compile regexp object for checking groupIDs
	groupIDRegexp := regexp.MustCompile(`\+.*:.*`)

	// Check each application service for any config errors
	for _, appservice := range derived.ApplicationServices {
		// Namespace-related checks
		for key, namespaceSlice := range appservice.NamespaceMap {
			for _, namespace := range namespaceSlice {
				if err := validateNamespace(&appservice, key, &namespace, groupIDRegexp); err != nil {
					return err
				}
			}
		}

		// Check if the url has trailing /'s. If so, remove them
		appservice.URL = strings.TrimRight(appservice.URL, "/")

		// Check if we've already seen this ID. No two application services
		// can have the same ID or token.
		if idMap[appservice.ID] {
			return ConfigErrors([]string{fmt.Sprintf(
				"Application service ID %s must be unique", appservice.ID,
			)})
		}
		// Check if we've already seen this token
		if tokenMap[appservice.ASToken] {
			return ConfigErrors([]string{fmt.Sprintf(
				"Application service Token %s must be unique", appservice.ASToken,
			)})
		}

		// Add the id/token to their respective maps if we haven't already
		// seen them.
		idMap[appservice.ID] = true
		tokenMap[appservice.ASToken] = true

		// TODO: Remove once rate_limited is implemented
		if appservice.RateLimited {
			log.Warn("WARNING: Application service option rate_limited is currently unimplemented")
		}
		// TODO: Remove once protocols is implemented
		if len(appservice.Protocols) > 0 {
			log.Warn("WARNING: Application service option protocols is currently unimplemented")
		}
	}

	return setupRegexps(config, derived)
}

// validateNamespace returns nil or an error based on whether a given
// application service namespace is valid. A namespace is valid if it has the
// required fields, and its regex is correct.
func validateNamespace(
	appservice *ApplicationService,
	key string,
	namespace *ApplicationServiceNamespace,
	groupIDRegexp *regexp.Regexp,
) error {
	// Check that namespace(s) are valid regex
	if !IsValidRegex(namespace.Regex) {
		return ConfigErrors([]string{fmt.Sprintf(
			"Invalid regex string for Application Service %s", appservice.ID,
		)})
	}

	// Check if GroupID for the users namespace is in the correct format
	if key == "users" && namespace.GroupID != "" {
		// TODO: Remove once group_id is implemented
		log.Warn("WARNING: Application service option group_id is currently unimplemented")

		correctFormat := groupIDRegexp.MatchString(namespace.GroupID)
		if !correctFormat {
			return ConfigErrors([]string{fmt.Sprintf(
				"Invalid user group_id field for application service %s.",
				appservice.ID,
			)})
		}
	}

	return nil
}

// IsValidRegex returns true or false based on whether the
// given string is valid regex or not
func IsValidRegex(regexString string) bool {
	_, err := regexp.Compile(regexString)

	return err == nil
}
<commit_msg>Treat the sender_localpart as an exclusive namespace of one user (#1790)<commit_after>// Copyright 2017 Andrew Morgan <andrew@amorgan.xyz>
//
// 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 config

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"strings"

	log "github.com/sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"
)

type AppServiceAPI struct {
	Matrix  *Global  `yaml:"-"`
	Derived *Derived `yaml:"-"` // TODO: Nuke Derived from orbit

	InternalAPI InternalAPIOptions `yaml:"internal_api"`

	Database DatabaseOptions `yaml:"database"`

	// DisableTLSValidation disables the validation of X.509 TLS certs
	// on appservice endpoints. This is not recommended in production!
	DisableTLSValidation bool `yaml:"disable_tls_validation"`

	ConfigFiles []string `yaml:"config_files"`
}

func (c *AppServiceAPI) Defaults() {
	c.InternalAPI.Listen = "http://localhost:7777"
	c.InternalAPI.Connect = "http://localhost:7777"
	c.Database.Defaults()
	c.Database.ConnectionString = "file:appservice.db"
}

func (c *AppServiceAPI) Verify(configErrs *ConfigErrors, isMonolith bool) {
	checkURL(configErrs, "app_service_api.internal_api.listen", string(c.InternalAPI.Listen))
	checkURL(configErrs, "app_service_api.internal_api.bind", string(c.InternalAPI.Connect))
	checkNotEmpty(configErrs, "app_service_api.database.connection_string", string(c.Database.ConnectionString))
}

// ApplicationServiceNamespace is the namespace that a specific application
// service has management over.
type ApplicationServiceNamespace struct {
	// Whether or not the namespace is managed solely by this application service
	Exclusive bool `yaml:"exclusive"`
	// A regex pattern that represents the namespace
	Regex string `yaml:"regex"`
	// The ID of an existing group that all users of this application service will
	// be added to. This field is only relevant to the `users` namespace.
	// Note that users who are joined to this group through an application service
	// are not to be listed when querying for the group's members, however the
	// group should be listed when querying an application service user's groups.
	// This is to prevent making spamming all users of an application service
	// trivial.
	GroupID string `yaml:"group_id"`
	// Regex object representing our pattern. Saves having to recompile every time
	RegexpObject *regexp.Regexp
}

// ApplicationService represents a Matrix application service.
// https://matrix.org/docs/spec/application_service/unstable.html
type ApplicationService struct {
	// User-defined, unique, persistent ID of the application service
	ID string `yaml:"id"`
	// Base URL of the application service
	URL string `yaml:"url"`
	// Application service token provided in requests to a homeserver
	ASToken string `yaml:"as_token"`
	// Homeserver token provided in requests to an application service
	HSToken string `yaml:"hs_token"`
	// Localpart of application service user
	SenderLocalpart string `yaml:"sender_localpart"`
	// Information about an application service's namespaces. Key is either
	// "users", "aliases" or "rooms"
	NamespaceMap map[string][]ApplicationServiceNamespace `yaml:"namespaces"`
	// Whether rate limiting is applied to each application service user
	RateLimited bool `yaml:"rate_limited"`
	// Any custom protocols that this application service provides (e.g. IRC)
	Protocols []string `yaml:"protocols"`
}

// IsInterestedInRoomID returns a bool on whether an application service's
// namespace includes the given room ID
func (a *ApplicationService) IsInterestedInRoomID(
	roomID string,
) bool {
	if namespaceSlice, ok := a.NamespaceMap["rooms"]; ok {
		for _, namespace := range namespaceSlice {
			if namespace.RegexpObject.MatchString(roomID) {
				return true
			}
		}
	}

	return false
}

// IsInterestedInUserID returns a bool on whether an application service's
// namespace includes the given user ID
func (a *ApplicationService) IsInterestedInUserID(
	userID string,
) bool {
	if namespaceSlice, ok := a.NamespaceMap["users"]; ok {
		for _, namespace := range namespaceSlice {
			if namespace.RegexpObject.MatchString(userID) {
				return true
			}
		}
	}

	return false
}

// OwnsNamespaceCoveringUserId returns a bool on whether an application service's
// namespace is exclusive and includes the given user ID
func (a *ApplicationService) OwnsNamespaceCoveringUserId(
	userID string,
) bool {
	if namespaceSlice, ok := a.NamespaceMap["users"]; ok {
		for _, namespace := range namespaceSlice {
			if namespace.Exclusive && namespace.RegexpObject.MatchString(userID) {
				return true
			}
		}
	}

	return false
}

// IsInterestedInRoomAlias returns a bool on whether an application service's
// namespace includes the given room alias
func (a *ApplicationService) IsInterestedInRoomAlias(
	roomAlias string,
) bool {
	if namespaceSlice, ok := a.NamespaceMap["aliases"]; ok {
		for _, namespace := range namespaceSlice {
			if namespace.RegexpObject.MatchString(roomAlias) {
				return true
			}
		}
	}

	return false
}

// loadAppServices iterates through all application service config files
// and loads their data into the config object for later access.
func loadAppServices(config *AppServiceAPI, derived *Derived) error {
	for _, configPath := range config.ConfigFiles {
		// Create a new application service with default options
		appservice := ApplicationService{
			RateLimited: true,
		}

		// Create an absolute path from a potentially relative path
		absPath, err := filepath.Abs(configPath)
		if err != nil {
			return err
		}

		// Read the application service's config file
		configData, err := ioutil.ReadFile(absPath)
		if err != nil {
			return err
		}

		// Load the config data into our struct
		if err = yaml.UnmarshalStrict(configData, &appservice); err != nil {
			return err
		}

		// Append the parsed application service to the global config
		derived.ApplicationServices = append(
			derived.ApplicationServices, appservice,
		)
	}

	// Check for any errors in the loaded application services
	return checkErrors(config, derived)
}

// setupRegexps will create regex objects for exclusive and non-exclusive
// usernames, aliases and rooms of all application services, so that other
// methods can quickly check if a particular string matches any of them.
func setupRegexps(asAPI *AppServiceAPI, derived *Derived) (err error) {
	// Combine all exclusive namespaces for later string checking
	var exclusiveUsernameStrings, exclusiveAliasStrings []string

	// If an application service's regex is marked as exclusive, add
	// its contents to the overall exlusive regex string. Room regex
	// not necessary as we aren't denying exclusive room ID creation
	for _, appservice := range derived.ApplicationServices {
		// The sender_localpart can be considered an exclusive regex for a single user, so let's do that
		// to simplify the code
		var senderUserIDSlice = []string{fmt.Sprintf("@%s:%s", appservice.SenderLocalpart, asAPI.Matrix.ServerName)}
		usersSlice, found := appservice.NamespaceMap["users"]
		if !found {
			usersSlice = []ApplicationServiceNamespace{}
			appservice.NamespaceMap["users"] = usersSlice
		}
		appendExclusiveNamespaceRegexs(&senderUserIDSlice, usersSlice)

		for key, namespaceSlice := range appservice.NamespaceMap {
			switch key {
			case "users":
				appendExclusiveNamespaceRegexs(&exclusiveUsernameStrings, namespaceSlice)
			case "aliases":
				appendExclusiveNamespaceRegexs(&exclusiveAliasStrings, namespaceSlice)
			}
		}
	}

	// Join the regexes together into one big regex.
	// i.e. "app1.*", "app2.*" -> "(app1.*)|(app2.*)"
	// Later we can check if a username or alias matches any exclusive regex and
	// deny access if it isn't from an application service
	exclusiveUsernames := strings.Join(exclusiveUsernameStrings, "|")
	exclusiveAliases := strings.Join(exclusiveAliasStrings, "|")

	// If there are no exclusive regexes, compile string so that it will not match
	// any valid usernames/aliases/roomIDs
	if exclusiveUsernames == "" {
		exclusiveUsernames = "^$"
	}
	if exclusiveAliases == "" {
		exclusiveAliases = "^$"
	}

	// Store compiled Regex
	if derived.ExclusiveApplicationServicesUsernameRegexp, err = regexp.Compile(exclusiveUsernames); err != nil {
		return err
	}
	if derived.ExclusiveApplicationServicesAliasRegexp, err = regexp.Compile(exclusiveAliases); err != nil {
		return err
	}

	return nil
}

// appendExclusiveNamespaceRegexs takes a slice of strings and a slice of
// namespaces and will append the regexes of only the exclusive namespaces
// into the string slice
func appendExclusiveNamespaceRegexs(
	exclusiveStrings *[]string, namespaces []ApplicationServiceNamespace,
) {
	for index, namespace := range namespaces {
		if namespace.Exclusive {
			// We append parenthesis to later separate each regex when we compile
			// i.e. "app1.*", "app2.*" -> "(app1.*)|(app2.*)"
			*exclusiveStrings = append(*exclusiveStrings, "("+namespace.Regex+")")
		}

		// Compile this regex into a Regexp object for later use
		namespaces[index].RegexpObject, _ = regexp.Compile(namespace.Regex)
	}
}

// checkErrors checks for any configuration errors amongst the loaded
// application services according to the application service spec.
func checkErrors(config *AppServiceAPI, derived *Derived) (err error) {
	var idMap = make(map[string]bool)
	var tokenMap = make(map[string]bool)

	// Compile regexp object for checking groupIDs
	groupIDRegexp := regexp.MustCompile(`\+.*:.*`)

	// Check each application service for any config errors
	for _, appservice := range derived.ApplicationServices {
		// Namespace-related checks
		for key, namespaceSlice := range appservice.NamespaceMap {
			for _, namespace := range namespaceSlice {
				if err := validateNamespace(&appservice, key, &namespace, groupIDRegexp); err != nil {
					return err
				}
			}
		}

		// Check if the url has trailing /'s. If so, remove them
		appservice.URL = strings.TrimRight(appservice.URL, "/")

		// Check if we've already seen this ID. No two application services
		// can have the same ID or token.
		if idMap[appservice.ID] {
			return ConfigErrors([]string{fmt.Sprintf(
				"Application service ID %s must be unique", appservice.ID,
			)})
		}
		// Check if we've already seen this token
		if tokenMap[appservice.ASToken] {
			return ConfigErrors([]string{fmt.Sprintf(
				"Application service Token %s must be unique", appservice.ASToken,
			)})
		}

		// Add the id/token to their respective maps if we haven't already
		// seen them.
		idMap[appservice.ID] = true
		tokenMap[appservice.ASToken] = true

		// TODO: Remove once rate_limited is implemented
		if appservice.RateLimited {
			log.Warn("WARNING: Application service option rate_limited is currently unimplemented")
		}
		// TODO: Remove once protocols is implemented
		if len(appservice.Protocols) > 0 {
			log.Warn("WARNING: Application service option protocols is currently unimplemented")
		}
	}

	return setupRegexps(config, derived)
}

// validateNamespace returns nil or an error based on whether a given
// application service namespace is valid. A namespace is valid if it has the
// required fields, and its regex is correct.
func validateNamespace(
	appservice *ApplicationService,
	key string,
	namespace *ApplicationServiceNamespace,
	groupIDRegexp *regexp.Regexp,
) error {
	// Check that namespace(s) are valid regex
	if !IsValidRegex(namespace.Regex) {
		return ConfigErrors([]string{fmt.Sprintf(
			"Invalid regex string for Application Service %s", appservice.ID,
		)})
	}

	// Check if GroupID for the users namespace is in the correct format
	if key == "users" && namespace.GroupID != "" {
		// TODO: Remove once group_id is implemented
		log.Warn("WARNING: Application service option group_id is currently unimplemented")

		correctFormat := groupIDRegexp.MatchString(namespace.GroupID)
		if !correctFormat {
			return ConfigErrors([]string{fmt.Sprintf(
				"Invalid user group_id field for application service %s.",
				appservice.ID,
			)})
		}
	}

	return nil
}

// IsValidRegex returns true or false based on whether the
// given string is valid regex or not
func IsValidRegex(regexString string) bool {
	_, err := regexp.Compile(regexString)

	return err == nil
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"
	"runtime"
)

const Binary = "0.3.7-HA.1.5.1"

func String(app string) string {
	return fmt.Sprintf("%s v%s (built w/%s)", app, Binary, runtime.Version())
}
<commit_msg>update version<commit_after>package version

import (
	"fmt"
	"runtime"
)

const Binary = "0.3.7-HA.1.5.2"

func String(app string) string {
	return fmt.Sprintf("%s v%s (built w/%s)", app, Binary, runtime.Version())
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/hoisie/web"
    "github.com/russross/blackfriday"
    "io/ioutil"
    "log"
    "os"
)

func handler(ctx *web.Context, path string) {
    input, err := ioutil.ReadFile("testdata/foo.md")
    if err != nil {
        ctx.NotFound("File Not Found\n" + err.Error())
        return
    }
    ctx.WriteString(string(blackfriday.MarkdownCommon(input)))
}

func main() {
    f, err := os.Create("server.log")
    if err != nil {
        println(err.Error())
        return
    }
    logger := log.New(f, "", log.Ldate|log.Ltime)
    web.Get("/(.*)", handler)
    web.SetLogger(logger)
    web.Run(":8080")
}
<commit_msg>Split handler into two code paths<commit_after>package main

import (
    "github.com/hoisie/web"
    "github.com/russross/blackfriday"
    "io/ioutil"
    "log"
    "os"
)

func handler(ctx *web.Context, path string) {
    if path == "" {
        ctx.WriteString("foo")
        return
    } else {
        input, err := ioutil.ReadFile(path)
        if err != nil {
            ctx.NotFound("File Not Found\n" + err.Error())
            return
        }
        ctx.WriteString(string(blackfriday.MarkdownCommon(input)))
        return
    }
    ctx.Abort(500, "Server Error")
}

func main() {
    f, err := os.Create("server.log")
    if err != nil {
        println(err.Error())
        return
    }
    logger := log.New(f, "", log.Ldate|log.Ltime)
    web.Get("/(.*)", handler)
    web.SetLogger(logger)
    web.Run(":8080")
}
<|endoftext|>