<commit_before>package etcd

import (
	"crypto/tls"
	"net"
	"net/http"
	"strings"
	"time"

	etcd "github.com/coreos/go-etcd/etcd"
	"github.com/docker/libkv/store"
)

// Etcd is the receiver type for the
// Store interface
type Etcd struct {
	client       *etcd.Client
	ephemeralTTL time.Duration
}

type etcdLock struct {
	client   *etcd.Client
	stopLock chan struct{}
	key      string
	value    string
	last     *etcd.Response
	ttl      uint64
}

const (
	periodicSync      = 10 * time.Minute
	defaultLockTTL    = 20 * time.Second
	defaultUpdateTime = 5 * time.Second
)

// New creates a new Etcd client given a list
// of endpoints and an optional tls config
func New(addrs []string, options *store.Config) (store.Store, error) {
	s := &Etcd{}

	entries := store.CreateEndpoints(addrs, "http")
	s.client = etcd.NewClient(entries)

	// Set options
	if options != nil {
		if options.TLS != nil {
			s.setTLS(options.TLS)
		}
		if options.ConnectionTimeout != 0 {
			s.setTimeout(options.ConnectionTimeout)
		}
		if options.EphemeralTTL != 0 {
			s.setEphemeralTTL(options.EphemeralTTL)
		}
	}

	// Periodic SyncCluster
	go func() {
		for {
			s.client.SyncCluster()
			time.Sleep(periodicSync)
		}
	}()

	return s, nil
}

// SetTLS sets the tls configuration given the path
// of certificate files
func (s *Etcd) setTLS(tls *tls.Config) {
	// Change to https scheme
	var addrs []string
	entries := s.client.GetCluster()
	for _, entry := range entries {
		addrs = append(addrs, strings.Replace(entry, "http", "https", -1))
	}
	s.client.SetCluster(addrs)

	// Set transport
	t := http.Transport{
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     tls,
	}
	s.client.SetTransport(&t)
}

// setTimeout sets the timeout used for connecting to the store
func (s *Etcd) setTimeout(time time.Duration) {
	s.client.SetDialTimeout(time)
}

// setEphemeralHeartbeat sets the heartbeat value to notify
// that a node is alive
func (s *Etcd) setEphemeralTTL(time time.Duration) {
	s.ephemeralTTL = time
}

// createDirectory creates the entire path for a directory
// that does not exist
func (s *Etcd) createDirectory(path string) error {
	if _, err := s.client.CreateDir(store.Normalize(path), 10); err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {
			// Skip key already exists
			if etcdError.ErrorCode != 105 {
				return err
			}
		} else {
			return err
		}
	}
	return nil
}

// Get the value at "key", returns the last modified index
// to use in conjunction to Atomic calls
func (s *Etcd) Get(key string) (pair *store.KVPair, err error) {
	result, err := s.client.Get(store.Normalize(key), false, false)
	if err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {
			// Not a Directory or Not a file
			if etcdError.ErrorCode == 102 || etcdError.ErrorCode == 104 {
				return nil, store.ErrKeyNotFound
			}
		}
		return nil, err
	}

	pair = &store.KVPair{
		Key:       key,
		Value:     []byte(result.Node.Value),
		LastIndex: result.Node.ModifiedIndex,
	}

	return pair, nil
}

// Put a value at "key"
func (s *Etcd) Put(key string, value []byte, opts *store.WriteOptions) error {

	// Default TTL = 0 means no expiration
	var ttl uint64
	if opts != nil && opts.Ephemeral {
		ttl = uint64(s.ephemeralTTL.Seconds())
	}

	if _, err := s.client.Set(key, string(value), ttl); err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {

			// Not a directory
			if etcdError.ErrorCode == 104 {
				// Remove the last element (the actual key)
				// and create the full directory path
				err = s.createDirectory(store.GetDirectory(key))
				if err != nil {
					return err
				}

				// Now that the directory is created, set the key
				if _, err := s.client.Set(key, string(value), ttl); err != nil {
					return err
				}
			}
		}
		return err
	}
	return nil
}

// Delete a value at "key"
func (s *Etcd) Delete(key string) error {
	_, err := s.client.Delete(store.Normalize(key), false)
	return err
}

// Exists checks if the key exists inside the store
func (s *Etcd) Exists(key string) (bool, error) {
	entry, err := s.Get(key)
	if err != nil && entry != nil {
		if err == store.ErrKeyNotFound || entry.Value == nil {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

// Watch for changes on a "key"
// It returns a channel that will receive changes or pass
// on errors. Upon creation, the current value will first
// be sent to the channel. Providing a non-nil stopCh can
// be used to stop watching.
func (s *Etcd) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) {
	// Get the current value
	current, err := s.Get(key)
	if err != nil {
		return nil, err
	}

	// Start an etcd watch.
	// Note: etcd will send the current value through the channel.
	etcdWatchCh := make(chan *etcd.Response)
	etcdStopCh := make(chan bool)
	go s.client.Watch(store.Normalize(key), 0, false, etcdWatchCh, etcdStopCh)

	// Adapter goroutine: The goal here is to convert whatever
	// format etcd is using into our interface.
	watchCh := make(chan *store.KVPair)
	go func() {
		defer close(watchCh)

		// Push the current value through the channel.
		watchCh <- current

		for {
			select {
			case result := <-etcdWatchCh:
				watchCh <- &store.KVPair{
					Key:       key,
					Value:     []byte(result.Node.Value),
					LastIndex: result.Node.ModifiedIndex,
				}
			case <-stopCh:
				etcdStopCh <- true
				return
			}
		}
	}()
	return watchCh, nil
}

// WatchTree watches for changes on a "directory"
// It returns a channel that will receive changes or pass
// on errors. Upon creating a watch, the current childs values
// will be sent to the channel .Providing a non-nil stopCh can
// be used to stop watching.
func (s *Etcd) WatchTree(directory string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) {
	// Get child values
	current, err := s.List(directory)
	if err != nil {
		return nil, err
	}

	// Start the watch
	etcdWatchCh := make(chan *etcd.Response)
	etcdStopCh := make(chan bool)
	go s.client.Watch(store.Normalize(directory), 0, true, etcdWatchCh, etcdStopCh)

	// Adapter goroutine: The goal here is to convert whatever
	// format etcd is using into our interface.
	watchCh := make(chan []*store.KVPair)
	go func() {
		defer close(watchCh)

		// Push the current value through the channel.
		watchCh <- current

		for {
			select {
			case <-etcdWatchCh:
				// FIXME: We should probably use the value pushed by the channel.
				// However, Node.Nodes seems to be empty.
				if list, err := s.List(directory); err == nil {
					watchCh <- list
				}
			case <-stopCh:
				etcdStopCh <- true
				return
			}
		}
	}()
	return watchCh, nil
}

// AtomicPut put a value at "key" if the key has not been
// modified in the meantime, throws an error if this is the case
func (s *Etcd) AtomicPut(key string, value []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) {
	if previous == nil {
		return false, nil, store.ErrPreviousNotSpecified
	}

	meta, err := s.client.CompareAndSwap(store.Normalize(key), string(value), 0, "", previous.LastIndex)
	if err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {
			// Compare Failed
			if etcdError.ErrorCode == 101 {
				return false, nil, store.ErrKeyModified
			}
		}
		return false, nil, err
	}

	updated := &store.KVPair{
		Key:       key,
		Value:     value,
		LastIndex: meta.Node.ModifiedIndex,
	}

	return true, updated, nil
}

// AtomicDelete deletes a value at "key" if the key
// has not been modified in the meantime, throws an
// error if this is the case
func (s *Etcd) AtomicDelete(key string, previous *store.KVPair) (bool, error) {
	if previous == nil {
		return false, store.ErrPreviousNotSpecified
	}

	_, err := s.client.CompareAndDelete(store.Normalize(key), "", previous.LastIndex)
	if err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {
			// Compare failed
			if etcdError.ErrorCode == 101 {
				return false, store.ErrKeyModified
			}
		}
		return false, err
	}

	return true, nil
}

// List child nodes of a given directory
func (s *Etcd) List(directory string) ([]*store.KVPair, error) {
	resp, err := s.client.Get(store.Normalize(directory), true, true)
	if err != nil {
		return nil, err
	}
	kv := []*store.KVPair{}
	for _, n := range resp.Node.Nodes {
		key := strings.TrimLeft(n.Key, "/")
		kv = append(kv, &store.KVPair{
			Key:       key,
			Value:     []byte(n.Value),
			LastIndex: n.ModifiedIndex,
		})
	}
	return kv, nil
}

// DeleteTree deletes a range of keys under a given directory
func (s *Etcd) DeleteTree(directory string) error {
	_, err := s.client.Delete(store.Normalize(directory), true)
	return err
}

// NewLock returns a handle to a lock struct which can
// be used to provide mutual exclusion on a key
func (s *Etcd) NewLock(key string, options *store.LockOptions) (lock store.Locker, err error) {
	var value string
	ttl := uint64(time.Duration(defaultLockTTL).Seconds())

	// Apply options on Lock
	if options != nil {
		if options.Value != nil {
			value = string(options.Value)
		}
		if options.TTL != 0 {
			ttl = uint64(options.TTL.Seconds())
		}
	}

	// Create lock object
	lock = &etcdLock{
		client: s.client,
		key:    key,
		value:  value,
		ttl:    ttl,
	}

	return lock, nil
}

// Lock attempts to acquire the lock and blocks while
// doing so. It returns a channel that is closed if our
// lock is lost or if an error occurs
func (l *etcdLock) Lock() (<-chan struct{}, error) {

	key := store.Normalize(l.key)

	// Lock holder channels
	lockHeld := make(chan struct{})
	stopLocking := make(chan struct{})

	var lastIndex uint64

	for {
		resp, err := l.client.Create(key, l.value, l.ttl)
		if err != nil {
			if etcdError, ok := err.(*etcd.EtcdError); ok {
				// Key already exists
				if etcdError.ErrorCode != 105 {
					lastIndex = ^uint64(0)
				}
			}
		} else {
			lastIndex = resp.Node.ModifiedIndex
		}

		_, err = l.client.CompareAndSwap(key, l.value, l.ttl, "", lastIndex)

		if err == nil {
			// Leader section
			l.stopLock = stopLocking
			go l.holdLock(key, lockHeld, stopLocking)
			break
		} else {
			// Seeker section
			chW := make(chan *etcd.Response)
			chWStop := make(chan bool)
			l.waitLock(key, chW, chWStop)

			// Delete or Expire event occured
			// Retry
		}
	}

	return lockHeld, nil
}

// Hold the lock as long as we can
// Updates the key ttl periodically until we receive
// an explicit stop signal from the Unlock method
func (l *etcdLock) holdLock(key string, lockHeld chan struct{}, stopLocking chan struct{}) {
	defer close(lockHeld)

	update := time.NewTicker(defaultUpdateTime)
	defer update.Stop()

	var err error

	for {
		select {
		case <-update.C:
			l.last, err = l.client.Update(key, l.value, l.ttl)
			if err != nil {
				return
			}

		case <-stopLocking:
			return
		}
	}
}

// WaitLock simply waits for the key to be available for creation
func (l *etcdLock) waitLock(key string, eventCh chan *etcd.Response, stopWatchCh chan bool) {
	go l.client.Watch(key, 0, false, eventCh, stopWatchCh)
	for event := range eventCh {
		if event.Action == "delete" || event.Action == "expire" {
			return
		}
	}
}

// Unlock the "key". Calling unlock while
// not holding the lock will throw an error
func (l *etcdLock) Unlock() error {
	if l.stopLock != nil {
		l.stopLock <- struct{}{}
	}
	if l.last != nil {
		_, err := l.client.CompareAndDelete(store.Normalize(l.key), l.value, l.last.Node.ModifiedIndex)
		if err != nil {
			return err
		}
	}
	return nil
}

// Close closes the client connection
func (s *Etcd) Close() {
	return
}
<commit_msg>AtomicPut last Index == 0 means atomic create.<commit_after>package etcd

import (
	"crypto/tls"
	"net"
	"net/http"
	"strings"
	"time"

	etcd "github.com/coreos/go-etcd/etcd"
	"github.com/docker/libkv/store"
)

// Etcd is the receiver type for the
// Store interface
type Etcd struct {
	client       *etcd.Client
	ephemeralTTL time.Duration
}

type etcdLock struct {
	client   *etcd.Client
	stopLock chan struct{}
	key      string
	value    string
	last     *etcd.Response
	ttl      uint64
}

const (
	periodicSync      = 10 * time.Minute
	defaultLockTTL    = 20 * time.Second
	defaultUpdateTime = 5 * time.Second
)

// New creates a new Etcd client given a list
// of endpoints and an optional tls config
func New(addrs []string, options *store.Config) (store.Store, error) {
	s := &Etcd{}

	entries := store.CreateEndpoints(addrs, "http")
	s.client = etcd.NewClient(entries)

	// Set options
	if options != nil {
		if options.TLS != nil {
			s.setTLS(options.TLS)
		}
		if options.ConnectionTimeout != 0 {
			s.setTimeout(options.ConnectionTimeout)
		}
		if options.EphemeralTTL != 0 {
			s.setEphemeralTTL(options.EphemeralTTL)
		}
	}

	// Periodic SyncCluster
	go func() {
		for {
			s.client.SyncCluster()
			time.Sleep(periodicSync)
		}
	}()

	return s, nil
}

// SetTLS sets the tls configuration given the path
// of certificate files
func (s *Etcd) setTLS(tls *tls.Config) {
	// Change to https scheme
	var addrs []string
	entries := s.client.GetCluster()
	for _, entry := range entries {
		addrs = append(addrs, strings.Replace(entry, "http", "https", -1))
	}
	s.client.SetCluster(addrs)

	// Set transport
	t := http.Transport{
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     tls,
	}
	s.client.SetTransport(&t)
}

// setTimeout sets the timeout used for connecting to the store
func (s *Etcd) setTimeout(time time.Duration) {
	s.client.SetDialTimeout(time)
}

// setEphemeralHeartbeat sets the heartbeat value to notify
// that a node is alive
func (s *Etcd) setEphemeralTTL(time time.Duration) {
	s.ephemeralTTL = time
}

// createDirectory creates the entire path for a directory
// that does not exist
func (s *Etcd) createDirectory(path string) error {
	if _, err := s.client.CreateDir(store.Normalize(path), 10); err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {
			// Skip key already exists
			if etcdError.ErrorCode != 105 {
				return err
			}
		} else {
			return err
		}
	}
	return nil
}

// Get the value at "key", returns the last modified index
// to use in conjunction to Atomic calls
func (s *Etcd) Get(key string) (pair *store.KVPair, err error) {
	result, err := s.client.Get(store.Normalize(key), false, false)
	if err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {
			// Not a Directory or Not a file
			if etcdError.ErrorCode == 102 || etcdError.ErrorCode == 104 {
				return nil, store.ErrKeyNotFound
			}
		}
		return nil, err
	}

	pair = &store.KVPair{
		Key:       key,
		Value:     []byte(result.Node.Value),
		LastIndex: result.Node.ModifiedIndex,
	}

	return pair, nil
}

// Put a value at "key"
func (s *Etcd) Put(key string, value []byte, opts *store.WriteOptions) error {

	// Default TTL = 0 means no expiration
	var ttl uint64
	if opts != nil && opts.Ephemeral {
		ttl = uint64(s.ephemeralTTL.Seconds())
	}

	if _, err := s.client.Set(key, string(value), ttl); err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {

			// Not a directory
			if etcdError.ErrorCode == 104 {
				// Remove the last element (the actual key)
				// and create the full directory path
				err = s.createDirectory(store.GetDirectory(key))
				if err != nil {
					return err
				}

				// Now that the directory is created, set the key
				if _, err := s.client.Set(key, string(value), ttl); err != nil {
					return err
				}
			}
		}
		return err
	}
	return nil
}

// Delete a value at "key"
func (s *Etcd) Delete(key string) error {
	_, err := s.client.Delete(store.Normalize(key), false)
	return err
}

// Exists checks if the key exists inside the store
func (s *Etcd) Exists(key string) (bool, error) {
	entry, err := s.Get(key)
	if err != nil && entry != nil {
		if err == store.ErrKeyNotFound || entry.Value == nil {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

// Watch for changes on a "key"
// It returns a channel that will receive changes or pass
// on errors. Upon creation, the current value will first
// be sent to the channel. Providing a non-nil stopCh can
// be used to stop watching.
func (s *Etcd) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) {
	// Get the current value
	current, err := s.Get(key)
	if err != nil {
		return nil, err
	}

	// Start an etcd watch.
	// Note: etcd will send the current value through the channel.
	etcdWatchCh := make(chan *etcd.Response)
	etcdStopCh := make(chan bool)
	go s.client.Watch(store.Normalize(key), 0, false, etcdWatchCh, etcdStopCh)

	// Adapter goroutine: The goal here is to convert whatever
	// format etcd is using into our interface.
	watchCh := make(chan *store.KVPair)
	go func() {
		defer close(watchCh)

		// Push the current value through the channel.
		watchCh <- current

		for {
			select {
			case result := <-etcdWatchCh:
				watchCh <- &store.KVPair{
					Key:       key,
					Value:     []byte(result.Node.Value),
					LastIndex: result.Node.ModifiedIndex,
				}
			case <-stopCh:
				etcdStopCh <- true
				return
			}
		}
	}()
	return watchCh, nil
}

// WatchTree watches for changes on a "directory"
// It returns a channel that will receive changes or pass
// on errors. Upon creating a watch, the current childs values
// will be sent to the channel .Providing a non-nil stopCh can
// be used to stop watching.
func (s *Etcd) WatchTree(directory string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) {
	// Get child values
	current, err := s.List(directory)
	if err != nil {
		return nil, err
	}

	// Start the watch
	etcdWatchCh := make(chan *etcd.Response)
	etcdStopCh := make(chan bool)
	go s.client.Watch(store.Normalize(directory), 0, true, etcdWatchCh, etcdStopCh)

	// Adapter goroutine: The goal here is to convert whatever
	// format etcd is using into our interface.
	watchCh := make(chan []*store.KVPair)
	go func() {
		defer close(watchCh)

		// Push the current value through the channel.
		watchCh <- current

		for {
			select {
			case <-etcdWatchCh:
				// FIXME: We should probably use the value pushed by the channel.
				// However, Node.Nodes seems to be empty.
				if list, err := s.List(directory); err == nil {
					watchCh <- list
				}
			case <-stopCh:
				etcdStopCh <- true
				return
			}
		}
	}()
	return watchCh, nil
}

// AtomicPut put a value at "key" if the key has not been
// modified in the meantime, throws an error if this is the case
func (s *Etcd) AtomicPut(key string, value []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) {
	if previous == nil {
		return false, nil, store.ErrPreviousNotSpecified
	}

	var meta *etcd.Response
	var err error
	if previous.LastIndex != 0 {
		meta, err = s.client.CompareAndSwap(store.Normalize(key), string(value), 0, "", previous.LastIndex)
	} else {
		// Interpret LastIndex == 0 as atomic create.
		meta, err = s.client.Create(store.Normalize(key), string(value), 0)
	}
	if err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {
			// Compare Failed
			if etcdError.ErrorCode == 101 {
				return false, nil, store.ErrKeyModified
			}
		}
		return false, nil, err
	}

	updated := &store.KVPair{
		Key:       key,
		Value:     value,
		LastIndex: meta.Node.ModifiedIndex,
	}

	return true, updated, nil
}

// AtomicDelete deletes a value at "key" if the key
// has not been modified in the meantime, throws an
// error if this is the case
func (s *Etcd) AtomicDelete(key string, previous *store.KVPair) (bool, error) {
	if previous == nil {
		return false, store.ErrPreviousNotSpecified
	}

	_, err := s.client.CompareAndDelete(store.Normalize(key), "", previous.LastIndex)
	if err != nil {
		if etcdError, ok := err.(*etcd.EtcdError); ok {
			// Compare failed
			if etcdError.ErrorCode == 101 {
				return false, store.ErrKeyModified
			}
		}
		return false, err
	}

	return true, nil
}

// List child nodes of a given directory
func (s *Etcd) List(directory string) ([]*store.KVPair, error) {
	resp, err := s.client.Get(store.Normalize(directory), true, true)
	if err != nil {
		return nil, err
	}
	kv := []*store.KVPair{}
	for _, n := range resp.Node.Nodes {
		key := strings.TrimLeft(n.Key, "/")
		kv = append(kv, &store.KVPair{
			Key:       key,
			Value:     []byte(n.Value),
			LastIndex: n.ModifiedIndex,
		})
	}
	return kv, nil
}

// DeleteTree deletes a range of keys under a given directory
func (s *Etcd) DeleteTree(directory string) error {
	_, err := s.client.Delete(store.Normalize(directory), true)
	return err
}

// NewLock returns a handle to a lock struct which can
// be used to provide mutual exclusion on a key
func (s *Etcd) NewLock(key string, options *store.LockOptions) (lock store.Locker, err error) {
	var value string
	ttl := uint64(time.Duration(defaultLockTTL).Seconds())

	// Apply options on Lock
	if options != nil {
		if options.Value != nil {
			value = string(options.Value)
		}
		if options.TTL != 0 {
			ttl = uint64(options.TTL.Seconds())
		}
	}

	// Create lock object
	lock = &etcdLock{
		client: s.client,
		key:    key,
		value:  value,
		ttl:    ttl,
	}

	return lock, nil
}

// Lock attempts to acquire the lock and blocks while
// doing so. It returns a channel that is closed if our
// lock is lost or if an error occurs
func (l *etcdLock) Lock() (<-chan struct{}, error) {

	key := store.Normalize(l.key)

	// Lock holder channels
	lockHeld := make(chan struct{})
	stopLocking := make(chan struct{})

	var lastIndex uint64

	for {
		resp, err := l.client.Create(key, l.value, l.ttl)
		if err != nil {
			if etcdError, ok := err.(*etcd.EtcdError); ok {
				// Key already exists
				if etcdError.ErrorCode != 105 {
					lastIndex = ^uint64(0)
				}
			}
		} else {
			lastIndex = resp.Node.ModifiedIndex
		}

		_, err = l.client.CompareAndSwap(key, l.value, l.ttl, "", lastIndex)

		if err == nil {
			// Leader section
			l.stopLock = stopLocking
			go l.holdLock(key, lockHeld, stopLocking)
			break
		} else {
			// Seeker section
			chW := make(chan *etcd.Response)
			chWStop := make(chan bool)
			l.waitLock(key, chW, chWStop)

			// Delete or Expire event occured
			// Retry
		}
	}

	return lockHeld, nil
}

// Hold the lock as long as we can
// Updates the key ttl periodically until we receive
// an explicit stop signal from the Unlock method
func (l *etcdLock) holdLock(key string, lockHeld chan struct{}, stopLocking chan struct{}) {
	defer close(lockHeld)

	update := time.NewTicker(defaultUpdateTime)
	defer update.Stop()

	var err error

	for {
		select {
		case <-update.C:
			l.last, err = l.client.Update(key, l.value, l.ttl)
			if err != nil {
				return
			}

		case <-stopLocking:
			return
		}
	}
}

// WaitLock simply waits for the key to be available for creation
func (l *etcdLock) waitLock(key string, eventCh chan *etcd.Response, stopWatchCh chan bool) {
	go l.client.Watch(key, 0, false, eventCh, stopWatchCh)
	for event := range eventCh {
		if event.Action == "delete" || event.Action == "expire" {
			return
		}
	}
}

// Unlock the "key". Calling unlock while
// not holding the lock will throw an error
func (l *etcdLock) Unlock() error {
	if l.stopLock != nil {
		l.stopLock <- struct{}{}
	}
	if l.last != nil {
		_, err := l.client.CompareAndDelete(store.Normalize(l.key), l.value, l.last.Node.ModifiedIndex)
		if err != nil {
			return err
		}
	}
	return nil
}

// Close closes the client connection
func (s *Etcd) Close() {
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2021 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 controller

import "net/http"

// Interface defines the base of a controller managed by a controller manager
type Interface interface {
	// Name returns the canonical name of the controller.
	Name() string
}

// Debuggable defines a controller that allows the controller manager
// to expose a debugging handler for the controller
//
// If a controller implements Debuggable, and the returned handler is
// not nil, the controller manager can mount the handler during startup.
type Debuggable interface {
	// DebuggingHandler returns a Handler that expose debugging information
	// for the controller, or nil if a debugging handler is not desired.
	//
	// The handler will be accessible at "/debug/controllers/{controllerName}/".
	DebuggingHandler() http.Handler
}
<commit_msg>HealthCheckable interface.<commit_after>/*
Copyright 2021 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 controller

import (
	"net/http"

	"k8s.io/controller-manager/pkg/healthz"
)

// Interface defines the base of a controller managed by a controller manager
type Interface interface {
	// Name returns the canonical name of the controller.
	Name() string
}

// Debuggable defines a controller that allows the controller manager
// to expose a debugging handler for the controller
//
// If a controller implements Debuggable, and the returned handler is
// not nil, the controller manager can mount the handler during startup.
type Debuggable interface {
	// DebuggingHandler returns a Handler that expose debugging information
	// for the controller, or nil if a debugging handler is not desired.
	//
	// The handler will be accessible at "/debug/controllers/{controllerName}/".
	DebuggingHandler() http.Handler
}

// HealthCheckable defines a controller that allows the controller manager
// to include it in the health checks.
//
// If a controller implements HealthCheckable, and the returned check
// is not nil, the controller manager can expose the check to the
// /healthz endpoint.
type HealthCheckable interface {
	// HealthChecker returns a UnnamedHealthChecker that the controller manager
	// can choose to mount on the /healthz endpoint, or nil if no custom
	// health check is desired.
	HealthChecker() healthz.UnnamedHealthChecker
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"github.com/astaxie/beego"
	"github.com/dockercn/docker-bucket/models"
	"net/http"
)

type DashboardController struct {
	beego.Controller
}

func (this *DashboardController) Prepare() {
	beego.Debug(fmt.Sprintf("[%s] %s | %s", this.Ctx.Input.Host(), this.Ctx.Input.Request.Method, this.Ctx.Input.Request.RequestURI))

	beego.Debug("[Header] ")
	beego.Debug(this.Ctx.Request.Header)
}

func (this *DashboardController) GetSetting() {
	beego.Error("ssssssss=", this.GetSession("user"))
	//加载session
	user, ok := this.GetSession("user").(models.User)
	if !ok {
		beego.Error(fmt.Sprintf("[WEB 用户] session加载失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"session加载失败\"}"))
		return
	}

	this.TplNames = "setting.html"

	this.Data["description"] = ""
	this.Data["author"] = ""
	this.Data["username"] = user.Username
	this.Render()
}

func (this *DashboardController) GetDashboard() {
	//加载session
	user, ok := this.GetSession("user").(models.User)
	if !ok {
		beego.Error(fmt.Sprintf("[WEB 用户] session加载失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"session加载失败\"}"))
		return
	}
	this.TplNames = "dashboard.html"
	this.Data["description"] = ""
	this.Data["author"] = ""
	this.Data["username"] = user.Username

	this.Render()
}
<commit_msg>add test<commit_after>package controllers

import (
	"fmt"
	"github.com/astaxie/beego"
	"github.com/dockercn/docker-bucket/models"
	"net/http"
)

type DashboardController struct {
	beego.Controller
}

func (this *DashboardController) Prepare() {
	beego.Debug(fmt.Sprintf("[%s] %s | %s", this.Ctx.Input.Host(), this.Ctx.Input.Request.Method, this.Ctx.Input.Request.RequestURI))

	beego.Debug("[Header] ")
	beego.Debug(this.Ctx.Request.Header)
}

func (this *DashboardController) GetSetting() {
	//加载session
	user, ok := this.GetSession("user").(models.User)
	if !ok {
		beego.Error(fmt.Sprintf("[WEB 用户] session加载失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"session加载失败\"}"))
		return
	}

	this.TplNames = "setting.html"

	this.Data["description"] = ""
	this.Data["author"] = ""
	this.Data["username"] = user.Username
	this.Render()
}

func (this *DashboardController) GetDashboard() {
	beego.Error("ssssssss=", this.GetSession("user"))
	//加载session
	user, ok := this.GetSession("user").(models.User)
	if !ok {
		beego.Error(fmt.Sprintf("[WEB 用户] session加载失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"session加载失败\"}"))
		return
	}
	this.TplNames = "dashboard.html"
	this.Data["description"] = ""
	this.Data["author"] = ""
	this.Data["username"] = user.Username

	this.Render()
}
<|endoftext|>


<commit_before>package controllers_test

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/antonve/logger-api/controllers"
	"github.com/antonve/logger-api/models"
	"github.com/antonve/logger-api/models/enums"
	"github.com/antonve/logger-api/utils"

	"github.com/labstack/echo"
	"github.com/stretchr/testify/assert"
)

type LogsBody struct {
	Logs []models.Log `json:"logs"`
}

func init() {
	utils.SetupTesting()
}

func TestPost(t *testing.T) {
	// Setup create log request
	e := echo.New()
	logBody := strings.NewReader(`{
    "language": "JA",
    "date": "2017-05-23",
    "duration": 25,
    "activity": "READING",
    "notes": {
      "type": "BOOK",
      "series": "キングダム",
      "volume": 1,
      "pages": 200
    }
  }`)
	req, err := http.NewRequest(echo.POST, "/api/logs", logBody)
	if !assert.NoError(t, err) {
		return
	}
	req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)

	if assert.NoError(t, controllers.APILogsPost(c)) {
		assert.Equal(t, http.StatusCreated, rec.Code)
		assert.Equal(t, `{"success": true}`, rec.Body.String())
	}
}

func TestGetByID(t *testing.T) {
	// Setup log to grab
	log := models.Log{Language: enums.LanguageKorean, Date: "2016-04-05", Duration: 60, Activity: enums.ActivityListening}
	logCollection := models.LogCollection{}
	id, _ := logCollection.Add(&log)

	// Setup log request
	e := echo.New()
	req := httptest.NewRequest(echo.GET, fmt.Sprintf("/api/logs/%d", id), nil)

	req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	c.SetPath("/api/logs/:id")
	c.SetParamNames("id")
	c.SetParamValues(fmt.Sprintf("%d", id))

	if assert.NoError(t, controllers.APILogsGetByID(c)) {
		// Check response
		var body models.Log
		assert.Equal(t, http.StatusOK, rec.Code)
		err := json.Unmarshal(rec.Body.Bytes(), &body)

		// Check if the log has information
		assert.Nil(t, err)
		assert.NotEmpty(t, body.Language)
		assert.NotEmpty(t, body.Date)
		assert.NotEmpty(t, body.Duration)
		assert.NotEmpty(t, body.Activity)

		// Check if the log has the correct information
		assert.Equal(t, enums.LanguageKorean, body.Language)
		assert.Equal(t, "2016-04-05", body.Date)
		assert.Equal(t, uint64(60), body.Duration)
		assert.Equal(t, enums.ActivityListening, body.Activity)
	}
}

func TestGetAll(t *testing.T) {
	// Setup log to grab
	logCollection := models.LogCollection{}
	var ids [3]uint64
	ids[0], _ = logCollection.Add(&models.Log{Language: enums.LanguageJapanese, Date: "2016-05-05", Duration: 30, Activity: enums.ActivityGrammar})
	ids[1], _ = logCollection.Add(&models.Log{Language: enums.LanguageMandarin, Date: "2016-04-03", Duration: 45, Activity: enums.ActivityOther})
	ids[2], _ = logCollection.Add(&models.Log{Language: enums.LanguageKorean, Date: "2016-04-05", Duration: 55, Activity: enums.ActivityTextbook})

	// Setup log request
	e := echo.New()
	req := httptest.NewRequest(echo.GET, "/", nil)

	req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	c.SetPath("/api/logs")

	if assert.NoError(t, controllers.APILogsGetAll(c)) {
		// Check response
		var body LogsBody
		assert.Equal(t, http.StatusOK, rec.Code)
		err := json.Unmarshal(rec.Body.Bytes(), &body)

		// Check if the log has information
		assert.Nil(t, err)
		assert.True(t, len(body.Logs) >= 3)
	}
}
<commit_msg>Fix url in TestGetAll in logs controller test<commit_after>package controllers_test

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/antonve/logger-api/controllers"
	"github.com/antonve/logger-api/models"
	"github.com/antonve/logger-api/models/enums"
	"github.com/antonve/logger-api/utils"

	"github.com/labstack/echo"
	"github.com/stretchr/testify/assert"
)

type LogsBody struct {
	Logs []models.Log `json:"logs"`
}

func init() {
	utils.SetupTesting()
}

func TestPost(t *testing.T) {
	// Setup create log request
	e := echo.New()
	logBody := strings.NewReader(`{
    "language": "JA",
    "date": "2017-05-23",
    "duration": 25,
    "activity": "READING",
    "notes": {
      "type": "BOOK",
      "series": "キングダム",
      "volume": 1,
      "pages": 200
    }
  }`)
	req, err := http.NewRequest(echo.POST, "/api/logs", logBody)
	if !assert.NoError(t, err) {
		return
	}
	req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)

	if assert.NoError(t, controllers.APILogsPost(c)) {
		assert.Equal(t, http.StatusCreated, rec.Code)
		assert.Equal(t, `{"success": true}`, rec.Body.String())
	}
}

func TestGetByID(t *testing.T) {
	// Setup log to grab
	log := models.Log{Language: enums.LanguageKorean, Date: "2016-04-05", Duration: 60, Activity: enums.ActivityListening}
	logCollection := models.LogCollection{}
	id, _ := logCollection.Add(&log)

	// Setup log request
	e := echo.New()
	req := httptest.NewRequest(echo.GET, fmt.Sprintf("/api/logs/%d", id), nil)

	req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	c.SetPath("/api/logs/:id")
	c.SetParamNames("id")
	c.SetParamValues(fmt.Sprintf("%d", id))

	if assert.NoError(t, controllers.APILogsGetByID(c)) {
		// Check response
		var body models.Log
		assert.Equal(t, http.StatusOK, rec.Code)
		err := json.Unmarshal(rec.Body.Bytes(), &body)

		// Check if the log has information
		assert.Nil(t, err)
		assert.NotEmpty(t, body.Language)
		assert.NotEmpty(t, body.Date)
		assert.NotEmpty(t, body.Duration)
		assert.NotEmpty(t, body.Activity)

		// Check if the log has the correct information
		assert.Equal(t, enums.LanguageKorean, body.Language)
		assert.Equal(t, "2016-04-05", body.Date)
		assert.Equal(t, uint64(60), body.Duration)
		assert.Equal(t, enums.ActivityListening, body.Activity)
	}
}

func TestGetAll(t *testing.T) {
	// Setup log to grab
	logCollection := models.LogCollection{}
	var ids [3]uint64
	ids[0], _ = logCollection.Add(&models.Log{Language: enums.LanguageJapanese, Date: "2016-05-05", Duration: 30, Activity: enums.ActivityGrammar})
	ids[1], _ = logCollection.Add(&models.Log{Language: enums.LanguageMandarin, Date: "2016-04-03", Duration: 45, Activity: enums.ActivityOther})
	ids[2], _ = logCollection.Add(&models.Log{Language: enums.LanguageKorean, Date: "2016-04-05", Duration: 55, Activity: enums.ActivityTextbook})

	// Setup log request
	e := echo.New()
	req := httptest.NewRequest(echo.GET, "/api/logs", nil)

	req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	c.SetPath("/api/logs")

	if assert.NoError(t, controllers.APILogsGetAll(c)) {
		// Check response
		var body LogsBody
		assert.Equal(t, http.StatusOK, rec.Code)
		err := json.Unmarshal(rec.Body.Bytes(), &body)

		// Check if the log has information
		assert.Nil(t, err)
		assert.True(t, len(body.Logs) >= 3)
	}
}
<|endoftext|>


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

package licenses

import (
	"context"
	"fmt"
	"go/build"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/google/go-licenses/internal/third_party/pkgsite/source"
	"golang.org/x/tools/go/packages"
)

// Library is a collection of packages covered by the same license file.
type Library struct {
	// LicensePath is the path of the file containing the library's license.
	LicensePath string
	// Packages contains import paths for Go packages in this library.
	// It may not be the complete set of all packages in the library.
	Packages []string
	// Parent go module.
	module *Module
}

// PackagesError aggregates all Packages[].Errors into a single error.
type PackagesError struct {
	pkgs []*packages.Package
}

func (e PackagesError) Error() string {
	var str strings.Builder
	str.WriteString(fmt.Sprintf("errors for %q:", e.pkgs))
	packages.Visit(e.pkgs, nil, func(pkg *packages.Package) {
		for _, err := range pkg.Errors {
			str.WriteString(fmt.Sprintf("\n%s: %s", pkg.PkgPath, err))
		}
	})
	return str.String()
}

// Libraries returns the collection of libraries used by this package, directly or transitively.
// A library is a collection of one or more packages covered by the same license file.
// Packages not covered by a license will be returned as individual libraries.
// Standard library packages will be ignored.
func Libraries(ctx context.Context, classifier Classifier, importPaths ...string) ([]*Library, error) {
	cfg := &packages.Config{
		Context: ctx,
		Mode:    packages.NeedImports | packages.NeedDeps | packages.NeedFiles | packages.NeedName | packages.NeedModule,
	}

	rootPkgs, err := packages.Load(cfg, importPaths...)
	if err != nil {
		return nil, err
	}

	pkgs := map[string]*packages.Package{}
	pkgsByLicense := make(map[string][]*packages.Package)
	errorOccurred := false
	packages.Visit(rootPkgs, func(p *packages.Package) bool {
		if len(p.Errors) > 0 {
			errorOccurred = true
			return false
		}
		if isStdLib(p) {
			// No license requirements for the Go standard library.
			return false
		}
		if len(p.OtherFiles) > 0 {
			glog.Warningf("%q contains non-Go code that can't be inspected for further dependencies:\n%s", p.PkgPath, strings.Join(p.OtherFiles, "\n"))
		}
		var pkgDir string
		switch {
		case len(p.GoFiles) > 0:
			pkgDir = filepath.Dir(p.GoFiles[0])
		case len(p.CompiledGoFiles) > 0:
			pkgDir = filepath.Dir(p.CompiledGoFiles[0])
		case len(p.OtherFiles) > 0:
			pkgDir = filepath.Dir(p.OtherFiles[0])
		default:
			// This package is empty - nothing to do.
			return true
		}
		licensePath, err := Find(pkgDir, p.Module.Dir, classifier)
		if err != nil {
			glog.Errorf("Failed to find license for %s: %v", p.PkgPath, err)
		}
		pkgs[p.PkgPath] = p
		pkgsByLicense[licensePath] = append(pkgsByLicense[licensePath], p)
		return true
	}, nil)
	if errorOccurred {
		return nil, PackagesError{
			pkgs: rootPkgs,
		}
	}

	var libraries []*Library
	for licensePath, pkgs := range pkgsByLicense {
		if licensePath == "" {
			// No license for these packages - return each one as a separate library.
			for _, p := range pkgs {
				libraries = append(libraries, &Library{
					Packages: []string{p.PkgPath},
					module:   newModule(p.Module),
				})
			}
			continue
		}
		lib := &Library{
			LicensePath: licensePath,
		}
		for _, pkg := range pkgs {
			lib.Packages = append(lib.Packages, pkg.PkgPath)
			if lib.module == nil && pkg.Module != nil {
				// All the sub packages should belong to the same module.
				lib.module = newModule(pkg.Module)
			}
		}
		if lib.module != nil && lib.module.Path != "" && lib.module.Dir == "" {
			// A known cause is that the module is vendored, so some information is lost.
			splits := strings.SplitN(lib.LicensePath, "/vendor/", 2)
			if len(splits) != 2 {
				glog.Warningf("module %s does not have dir and it's not vendored, cannot discover the license URL. Report to go-licenses developer if you see this.", lib.module.Path)
			} else {
				// This is vendored. Handle this known special case.

				// Extra note why we identify a vendored package like this.
				//
				// For a normal package:
				// * if it's not in a module, lib.module == nil
				// * if it's in a module, lib.module.Dir != ""
				// Only vendored modules will have lib.module != nil && lib.module.Path != "" && lib.module.Dir == "" as far as I know.
				// So the if condition above is already very strict for vendored packages.
				// On top of it, we checked the lib.LicensePath contains a vendor folder in it.
				// So it's rare to have a false positive for both conditions at the same time, although it may happen in theory.
				//
				// These assumptions may change in the future,
				// so we need to keep this updated with go tooling changes.
				parentModDir := splits[0]
				var parentPkg *packages.Package
				for _, rootPkg := range rootPkgs {
					if rootPkg.Module != nil && rootPkg.Module.Dir == parentModDir {
						parentPkg = rootPkg
						break
					}
				}
				if parentPkg == nil {
					glog.Warningf("cannot find parent package of vendored module %s", lib.module.Path)
				} else {
					// Vendored modules should be commited in the parent module, so it counts as part of the
					// parent module.
					lib.module = newModule(parentPkg.Module)
				}
			}
		}
		libraries = append(libraries, lib)
	}
	// Sort libraries to produce a stable result for snapshot diffing.
	sort.Slice(libraries, func(i, j int) bool {
		return libraries[i].Name() < libraries[j].Name()
	})
	return libraries, nil
}

// Name is the common prefix of the import paths for all of the packages in this library.
func (l *Library) Name() string {
	return commonAncestor(l.Packages)
}

func commonAncestor(paths []string) string {
	if len(paths) == 0 {
		return ""
	}
	if len(paths) == 1 {
		return paths[0]
	}
	sort.Strings(paths)
	min, max := paths[0], paths[len(paths)-1]
	lastSlashIndex := 0
	for i := 0; i < len(min) && i < len(max); i++ {
		if min[i] != max[i] {
			return min[:lastSlashIndex]
		}
		if min[i] == '/' {
			lastSlashIndex = i
		}
	}
	return min
}

func (l *Library) String() string {
	return l.Name()
}

// FileURL attempts to determine the URL for a file in this library using
// go module name and version.
func (l *Library) FileURL(ctx context.Context, filePath string) (string, error) {
	if l == nil {
		return "", fmt.Errorf("library is nil")
	}
	wrap := func(err error) error {
		return fmt.Errorf("getting file URL in library %s: %w", l.Name(), err)
	}
	m := l.module
	if m == nil {
		return "", wrap(fmt.Errorf("empty go module info"))
	}
	if m.Dir == "" {
		return "", wrap(fmt.Errorf("empty go module dir"))
	}
	client := source.NewClient(time.Second * 20)
	remote, err := source.ModuleInfo(ctx, client, m.Path, m.Version)
	if err != nil {
		return "", wrap(err)
	}
	if m.Version == "" {
		// This always happens for the module in development.
		// Note#1 if we pass version=HEAD to source.ModuleInfo, github tag for modules not at the root
		// of the repo will be incorrect, because there's a convention that:
		// * I have a module at github.com/google/go-licenses/submod.
		// * The module is of version v1.0.0.
		// Then the github tag should be submod/v1.0.0.
		// In our case, if we pass HEAD as version, the result commit will be submod/HEAD which is incorrect.
		// Therefore, to workaround this problem, we directly set the commit after getting module info.
		//
		// Note#2 repos have different branches as default, some use the
		// master branch and some use the main branch. However, HEAD
		// always refers to the default branch, so it's better than
		// both of master/main when we do not know which branch is default.
		// Examples:
		// * https://github.com/google/go-licenses/blob/HEAD/LICENSE
		// points to latest commit of master branch.
		// * https://github.com/google/licenseclassifier/blob/HEAD/LICENSE
		// points to latest commit of main branch.
		remote.SetCommit("HEAD")
		glog.Warningf("module %s has empty version, defaults to HEAD. The license URL may be incorrect. Please verify!", m.Path)
	}
	relativePath, err := filepath.Rel(m.Dir, filePath)
	if err != nil {
		return "", wrap(err)
	}
	// TODO: there are still rare cases this may result in an incorrect URL.
	// https://github.com/google/go-licenses/issues/73#issuecomment-1005587408
	return remote.FileURL(relativePath), nil
}

// isStdLib returns true if this package is part of the Go standard library.
func isStdLib(pkg *packages.Package) bool {
	if pkg.Name == "unsafe" {
		// Special case unsafe stdlib, because it does not contain go files.
		return true
	}
	if len(pkg.GoFiles) == 0 {
		return false
	}
	return strings.HasPrefix(pkg.GoFiles[0], build.Default.GOROOT)
}
<commit_msg>fix: segmentation fault panic when go modules info is missing (#129)<commit_after>// Copyright 2019 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package licenses

import (
	"context"
	"fmt"
	"go/build"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/google/go-licenses/internal/third_party/pkgsite/source"
	"golang.org/x/tools/go/packages"
)

// Library is a collection of packages covered by the same license file.
type Library struct {
	// LicensePath is the path of the file containing the library's license.
	LicensePath string
	// Packages contains import paths for Go packages in this library.
	// It may not be the complete set of all packages in the library.
	Packages []string
	// Parent go module.
	module *Module
}

// PackagesError aggregates all Packages[].Errors into a single error.
type PackagesError struct {
	pkgs []*packages.Package
}

func (e PackagesError) Error() string {
	var str strings.Builder
	str.WriteString(fmt.Sprintf("errors for %q:", e.pkgs))
	packages.Visit(e.pkgs, nil, func(pkg *packages.Package) {
		for _, err := range pkg.Errors {
			str.WriteString(fmt.Sprintf("\n%s: %s", pkg.PkgPath, err))
		}
	})
	return str.String()
}

// Libraries returns the collection of libraries used by this package, directly or transitively.
// A library is a collection of one or more packages covered by the same license file.
// Packages not covered by a license will be returned as individual libraries.
// Standard library packages will be ignored.
func Libraries(ctx context.Context, classifier Classifier, importPaths ...string) ([]*Library, error) {
	cfg := &packages.Config{
		Context: ctx,
		Mode:    packages.NeedImports | packages.NeedDeps | packages.NeedFiles | packages.NeedName | packages.NeedModule,
	}

	rootPkgs, err := packages.Load(cfg, importPaths...)
	if err != nil {
		return nil, err
	}

	pkgs := map[string]*packages.Package{}
	pkgsByLicense := make(map[string][]*packages.Package)
	pkgErrorOccurred := false
	otherErrorOccurred := false
	packages.Visit(rootPkgs, func(p *packages.Package) bool {
		if len(p.Errors) > 0 {
			pkgErrorOccurred = true
			return false
		}
		if isStdLib(p) {
			// No license requirements for the Go standard library.
			return false
		}
		if len(p.OtherFiles) > 0 {
			glog.Warningf("%q contains non-Go code that can't be inspected for further dependencies:\n%s", p.PkgPath, strings.Join(p.OtherFiles, "\n"))
		}
		var pkgDir string
		switch {
		case len(p.GoFiles) > 0:
			pkgDir = filepath.Dir(p.GoFiles[0])
		case len(p.CompiledGoFiles) > 0:
			pkgDir = filepath.Dir(p.CompiledGoFiles[0])
		case len(p.OtherFiles) > 0:
			pkgDir = filepath.Dir(p.OtherFiles[0])
		default:
			// This package is empty - nothing to do.
			return true
		}
		if p.Module == nil {
			otherErrorOccurred = true
			glog.Errorf("Package %s does not have module info. Non go modules projects are no longer supported. For feedback, refer to https://github.com/google/go-licenses/issues/128.", p.PkgPath)
			return false
		}
		licensePath, err := Find(pkgDir, p.Module.Dir, classifier)
		if err != nil {
			glog.Errorf("Failed to find license for %s: %v", p.PkgPath, err)
		}
		pkgs[p.PkgPath] = p
		pkgsByLicense[licensePath] = append(pkgsByLicense[licensePath], p)
		return true
	}, nil)
	if pkgErrorOccurred {
		return nil, PackagesError{
			pkgs: rootPkgs,
		}
	}
	if otherErrorOccurred {
		return nil, fmt.Errorf("some errors occurred when loading direct and transitive dependency packages")
	}

	var libraries []*Library
	for licensePath, pkgs := range pkgsByLicense {
		if licensePath == "" {
			// No license for these packages - return each one as a separate library.
			for _, p := range pkgs {
				libraries = append(libraries, &Library{
					Packages: []string{p.PkgPath},
					module:   newModule(p.Module),
				})
			}
			continue
		}
		lib := &Library{
			LicensePath: licensePath,
		}
		for _, pkg := range pkgs {
			lib.Packages = append(lib.Packages, pkg.PkgPath)
			if lib.module == nil && pkg.Module != nil {
				// All the sub packages should belong to the same module.
				lib.module = newModule(pkg.Module)
			}
		}
		if lib.module != nil && lib.module.Path != "" && lib.module.Dir == "" {
			// A known cause is that the module is vendored, so some information is lost.
			splits := strings.SplitN(lib.LicensePath, "/vendor/", 2)
			if len(splits) != 2 {
				glog.Warningf("module %s does not have dir and it's not vendored, cannot discover the license URL. Report to go-licenses developer if you see this.", lib.module.Path)
			} else {
				// This is vendored. Handle this known special case.

				// Extra note why we identify a vendored package like this.
				//
				// For a normal package:
				// * if it's not in a module, lib.module == nil
				// * if it's in a module, lib.module.Dir != ""
				// Only vendored modules will have lib.module != nil && lib.module.Path != "" && lib.module.Dir == "" as far as I know.
				// So the if condition above is already very strict for vendored packages.
				// On top of it, we checked the lib.LicensePath contains a vendor folder in it.
				// So it's rare to have a false positive for both conditions at the same time, although it may happen in theory.
				//
				// These assumptions may change in the future,
				// so we need to keep this updated with go tooling changes.
				parentModDir := splits[0]
				var parentPkg *packages.Package
				for _, rootPkg := range rootPkgs {
					if rootPkg.Module != nil && rootPkg.Module.Dir == parentModDir {
						parentPkg = rootPkg
						break
					}
				}
				if parentPkg == nil {
					glog.Warningf("cannot find parent package of vendored module %s", lib.module.Path)
				} else {
					// Vendored modules should be commited in the parent module, so it counts as part of the
					// parent module.
					lib.module = newModule(parentPkg.Module)
				}
			}
		}
		libraries = append(libraries, lib)
	}
	// Sort libraries to produce a stable result for snapshot diffing.
	sort.Slice(libraries, func(i, j int) bool {
		return libraries[i].Name() < libraries[j].Name()
	})
	return libraries, nil
}

// Name is the common prefix of the import paths for all of the packages in this library.
func (l *Library) Name() string {
	return commonAncestor(l.Packages)
}

func commonAncestor(paths []string) string {
	if len(paths) == 0 {
		return ""
	}
	if len(paths) == 1 {
		return paths[0]
	}
	sort.Strings(paths)
	min, max := paths[0], paths[len(paths)-1]
	lastSlashIndex := 0
	for i := 0; i < len(min) && i < len(max); i++ {
		if min[i] != max[i] {
			return min[:lastSlashIndex]
		}
		if min[i] == '/' {
			lastSlashIndex = i
		}
	}
	return min
}

func (l *Library) String() string {
	return l.Name()
}

// FileURL attempts to determine the URL for a file in this library using
// go module name and version.
func (l *Library) FileURL(ctx context.Context, filePath string) (string, error) {
	if l == nil {
		return "", fmt.Errorf("library is nil")
	}
	wrap := func(err error) error {
		return fmt.Errorf("getting file URL in library %s: %w", l.Name(), err)
	}
	m := l.module
	if m == nil {
		return "", wrap(fmt.Errorf("empty go module info"))
	}
	if m.Dir == "" {
		return "", wrap(fmt.Errorf("empty go module dir"))
	}
	client := source.NewClient(time.Second * 20)
	remote, err := source.ModuleInfo(ctx, client, m.Path, m.Version)
	if err != nil {
		return "", wrap(err)
	}
	if m.Version == "" {
		// This always happens for the module in development.
		// Note#1 if we pass version=HEAD to source.ModuleInfo, github tag for modules not at the root
		// of the repo will be incorrect, because there's a convention that:
		// * I have a module at github.com/google/go-licenses/submod.
		// * The module is of version v1.0.0.
		// Then the github tag should be submod/v1.0.0.
		// In our case, if we pass HEAD as version, the result commit will be submod/HEAD which is incorrect.
		// Therefore, to workaround this problem, we directly set the commit after getting module info.
		//
		// Note#2 repos have different branches as default, some use the
		// master branch and some use the main branch. However, HEAD
		// always refers to the default branch, so it's better than
		// both of master/main when we do not know which branch is default.
		// Examples:
		// * https://github.com/google/go-licenses/blob/HEAD/LICENSE
		// points to latest commit of master branch.
		// * https://github.com/google/licenseclassifier/blob/HEAD/LICENSE
		// points to latest commit of main branch.
		remote.SetCommit("HEAD")
		glog.Warningf("module %s has empty version, defaults to HEAD. The license URL may be incorrect. Please verify!", m.Path)
	}
	relativePath, err := filepath.Rel(m.Dir, filePath)
	if err != nil {
		return "", wrap(err)
	}
	// TODO: there are still rare cases this may result in an incorrect URL.
	// https://github.com/google/go-licenses/issues/73#issuecomment-1005587408
	return remote.FileURL(relativePath), nil
}

// isStdLib returns true if this package is part of the Go standard library.
func isStdLib(pkg *packages.Package) bool {
	if pkg.Name == "unsafe" {
		// Special case unsafe stdlib, because it does not contain go files.
		return true
	}
	if len(pkg.GoFiles) == 0 {
		return false
	}
	return strings.HasPrefix(pkg.GoFiles[0], build.Default.GOROOT)
}
<|endoftext|>


<commit_before>package gocardless_pro_go

type RefundService service

type Refund struct {
	ID							string				`json:"id,omitempty"`
	CreatedAt					string				`json:"created_at,omitempty"`
	Reference 					string				`json:"reference,omitempty"`
	Currency					string				`json:"currency,omitempty"`
	Amount						int64				`json:"amount,omitempty"`
	Links						[]RefundLink		`json:"links,omitempty"`
	Metadata					map[string]string	`json:"metadata,omitempty"`
}
<commit_msg>Add structure to create refund in refund service<commit_after>package gocardless_pro_go

import "fmt"

type RefundService service

type Refund struct {
	ID							string				`json:"id,omitempty"`
	CreatedAt					string				`json:"created_at,omitempty"`
	Reference 					string				`json:"reference,omitempty"`
	Currency					string				`json:"currency,omitempty"`
	Amount						int64				`json:"amount,omitempty"`
	Links						[]RefundLink		`json:"links,omitempty"`
	Metadata					map[string]string	`json:"metadata,omitempty"`
}

type RefundListRequest struct {
	CreatedAt 			CreatedAt	`json:"created_at,omitempty"`
	Limit				int			`json:"limit,omitempty"`
	Before				string		`json:"before,omitempty"`
	After				string		`json:"after,omitempty"`
	Creditor			string		`json:"creditor,omitempty"`
	Customer			string		`json:"customer,omitempty"`
	Status				string		`json:"status,omitempty"`
	Currency			string		`json:"currency,omitempty"`
	Mandate				string		`json:"mandate,omitempty"`
	Subscription		string		`json:"subscription,omitempty"`
}

type RefundList struct {
	Meta   ListMeta
	Values []Refund `json:"data"`
}

type RefundCreateRequest struct {
	Metadata				map[string]string		`json:"metadata,omitempty"`
	Reference				string					`json:"reference,omitempty"`
	Amount					string					`json:"amount,omitempty"`
	TotalAmountConfirmation	string					`json:"total_amount_confirmation,omitempty"`
	Links					[]string				`json:"links,omitempty"`
}

// Create creates a new refund
func (s *RefundService) CreateRefund(refundReq *RefundCreateRequest) (*Refund, error) {
	u := fmt.Sprintf("/refunds")
	refund := &Refund{}
	err := s.client.Call("POST", u, refundReq, refund)

	return refund, err
}

// List returns a list of refund
func (s *PaymentService) ListPayments(req *PaymentListRequest) (*PaymentList, error) {
	return s.ListNPayments(10, 0, req)
}

func (s *PaymentService) ListNPayments(count, offset int, req *PaymentListRequest) (*PaymentList, error) {
	params := url.Values{}
	params.Add("after", req.After)
	params.Add("before", req.Before)
	params.Add("created_at[gt]", req.CreatedAt.Gt)
	params.Add("created_at[gte]", req.CreatedAt.Gte)
	params.Add("created_at[lt]", req.CreatedAt.Lt)
	params.Add("created_at[lte]", req.CreatedAt.Lte)
	params.Add("limit", string(req.Limit))
	params.Add("status", req.Status)
	params.Add("mandate", req.Mandate)
	params.Add("customer", req.Customer)
	params.Add("creditor", req.Creditor)
	params.Add("currency", req.Currency)
	params.Add("subscription", req.Subscription)

	u := paginateURL("/payments", count, offset)
	payments := &PaymentList{}
	err := s.client.Call("GET", u, params, payments)

	return payments, err
}

func (s *PaymentService) GetPayment(id string) (*Payment, error) {
	u := fmt.Sprintf("/payments/%s", id)
	payment := &Payment{}
	err := s.client.Call("GET", u, nil, payment)

	return payment, err
}

func (s *PaymentService) UpdatePayment(updatedPayment *Payment, metadata map[string]string) (*Payment, error) {
	params := url.Values{}
	params.Add("metadata", string(metadata))
	u := fmt.Sprintf("payments/%d", updatedPayment.ID)
	payment := &Payment{}
	err := s.client.Call("PUT", u, params, payment)

	return payment, err
}
<|endoftext|>


<commit_before>package feiertage

import (
	"fmt"
	"sort"
)

// Region represents a Federal State of Germany (Bundesland). Some of the public holidays in
// Germany are common throughout the whole country, while other depend of the Bundesland.
type Region struct {
	Name      string
	Shortname string
	Feiertage []Feiertag
}

// String returns a String containing the name and Shortname of the region and the list of
// public holidays (objects of type Feiertage).
func (r Region) String() string {
	s := fmt.Sprintf("%s (%s)", r.Name, r.Shortname)
	for _, f := range r.Feiertage {
		s = fmt.Sprintf("%s\n  %s", s, f)
	}
	return s
}

func createFeiertagsList(y int, ffun []func(int) Feiertag) []Feiertag {
	feiern := []func(int) Feiertag{Neujahr, Karfreitag, Ostermontag,
		TagDerArbeit, ChristiHimmelfahrt, PfingstMontag,
		TagDerDeutschenEinheit, Weihnachten, ZweiterWeihnachtsfeiertag}
	// in 2017 the Reformationstag is a one time feiertag in all states
	if y == 2017 {
		feiern = append(feiern, Reformationstag)
	}

	for _, f := range ffun {
		if (y != 2017) || (f(y).Text != Reformationstag(y).Text) {
			feiern = append(feiern, f)
		}
	}
	feiertermine := []Feiertag{}
	for _, f := range feiern {
		feiertermine = append(feiertermine, f(y))
	}
	sort.Sort(ByDate(feiertermine))
	return feiertermine
}

// BadenWürttemberg returns a Region object holding all public holidays in the state
// Baden-Württemberg
func BadenWürttemberg(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Epiphanias, Fronleichnam, Allerheiligen}
	return Region{"Baden-Württemberg", "BW", createFeiertagsList(y, ffun)}
}

// Bayern returns a Region object holding all public holidays in the state Bayern
func Bayern(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Epiphanias, Fronleichnam, Allerheiligen}
	return Region{"Bayern", "BY", createFeiertagsList(y, ffun)}
}

// Berlin returns a Region object holding all public holidays in the state Berlin
func Berlin(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Berlin", "BE", createFeiertagsList(y, ffun)}
}

// Brandenburg returns a Region object holding all public holidays in the state
// Brandenburg
func Brandenburg(y int, inklSonntage ...bool) Region {
	var ffun []func(int) Feiertag
	if len(inklSonntage) > 0 && inklSonntage[0] == false {
		ffun = []func(int) Feiertag{Reformationstag}
	} else {
		ffun = []func(int) Feiertag{Ostern, Pfingsten, Reformationstag}
	}
	return Region{"Brandenburg", "BB", createFeiertagsList(y, ffun)}
}

// Bremen returns a Region object holding all public holidays in the state Bremen
func Bremen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Bremen", "HB", createFeiertagsList(y, ffun)}
}

// Hamburg returns a Region object holding all public holidays in the state Hamburg
func Hamburg(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Hamburg", "HH", createFeiertagsList(y, ffun)}
}

// Hessen returns a Region object holding all public holidays in the state Hessen
func Hessen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Fronleichnam}
	return Region{"Hessen", "HE", createFeiertagsList(y, ffun)}
}

// MecklenburgVorpommern returns a Region object holding all public holidays in
// the state Mecklenburg-Vorpommern
func MecklenburgVorpommern(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Reformationstag}
	return Region{"Mecklenburg-Vorpommern", "MV", createFeiertagsList(y, ffun)}
}

// Niedersachsen returns a Region object holding all public holidays in the
// state Niedersachsen
func Niedersachsen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Niedersachsen", "NI", createFeiertagsList(y, ffun)}
}

// NordrheinWestfalen returns a Region object holding all public holidays in the
// state Nordrhein-Westfalen
func NordrheinWestfalen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Fronleichnam, Allerheiligen}
	return Region{"Nordrhein-Westfalen", "NW", createFeiertagsList(y, ffun)}
}

// RheinlandPfalz returns a Region object holding all public holidays in the
// state Rheinland-Pfalz
func RheinlandPfalz(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Fronleichnam, Allerheiligen}
	return Region{"Rheinland-Pfalz", "RP", createFeiertagsList(y, ffun)}
}

// Saarland returns a Region object holding all public holidays in the state Saarland
func Saarland(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Fronleichnam, MariäHimmelfahrt, Allerheiligen}
	return Region{"Saarland", "SL", createFeiertagsList(y, ffun)}
}

// Sachsen returns a Region object holding all public holidays in the state Sachsen
func Sachsen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Reformationstag, BußUndBettag}
	return Region{"Sachsen", "SN", createFeiertagsList(y, ffun)}
}

// SachsenAnhalt returns a Region object holding all public holidays in the state SachsenAnhalt
func SachsenAnhalt(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Epiphanias, Reformationstag}
	return Region{"Sachsen-Anhalt", "ST", createFeiertagsList(y, ffun)}
}

// SchleswigHolstein returns a Region object holding all public holidays in the state SchleswigHolstein
func SchleswigHolstein(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Schleswig-Holstein", "SH", createFeiertagsList(y, ffun)}
}

// Thüringen returns a Region object holding all public holidays in the state Thüringen
func Thüringen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{
		Reformationstag}
	return Region{"Thüringen", "TH", createFeiertagsList(y, ffun)}
}

// Deutschland returns a Region object holding all public holidays that are Common in Germany
func Deutschland(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}

	return Region{"Deutschland", "DE", createFeiertagsList(y, ffun)}
}

// All returns a Region object holding all public holidays/feast days known to this program.
// Not all of htem are public holidays (basically 'free').
func All(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Epiphanias, HeiligeDreiKönige, Valentinstag,
		Weiberfastnacht, Rosenmontag, Fastnacht, Aschermittwoch, Gründonnerstag,
		BeginnSommerzeit, Walpurgisnacht, TagDerBefreiung, Muttertag, Vatertag, Fronleichnam,
		MariäHimmelfahrt, Reformationstag, Halloween, BeginnWinterzeit,
		Allerheiligen, Allerseelen, Martinstag, Karnevalsbeginn, BußUndBettag, Thanksgiving,
		Blackfriday, Volkstrauertag, Nikolaus, MariäUnbefleckteEmpfängnis, Heiligabend, Silvester}

	if len(inklSonntage) == 0 || inklSonntage[0] == true {
		ffun = append(ffun, Karnevalssonntag, Palmsonntag, Ostern, Pfingsten,
			Dreifaltigkeitssonntag, Erntedankfest, Totensonntag, ErsterAdvent, ZweiterAdvent,
			DritterAdvent, VierterAdvent)
	}

	return Region{"Alle", "All", createFeiertagsList(y, ffun)}
}
<commit_msg>Adding tagged release.<commit_after>package feiertage

import (
	"fmt"
	"sort"
)

// Region represents a Federal State of Germany (Bundesland). Some of the public holidays in
// Germany are common throughout the whole country, while other depend of the Bundesland.
type Region struct {
	Name      string
	Shortname string
	Feiertage []Feiertag
}

// String returns a String containing the name and Shortname of the region and the list of
// public holidays (objects of type Feiertage).
func (r Region) String() string {
	s := fmt.Sprintf("%s (%s)", r.Name, r.Shortname)
	for _, f := range r.Feiertage {
		s = fmt.Sprintf("%s\n  %s", s, f)
	}
	return s
}

func createFeiertagsList(y int, ffun []func(int) Feiertag) []Feiertag {
	feiern := []func(int) Feiertag{Neujahr, Karfreitag, Ostermontag,
		TagDerArbeit, ChristiHimmelfahrt, PfingstMontag,
		TagDerDeutschenEinheit, Weihnachten, ZweiterWeihnachtsfeiertag}
	// in 2017 the Reformationstag is a one time Feiertag in all states
	if y == 2017 {
		feiern = append(feiern, Reformationstag)
	}

	for _, f := range ffun {
		if (y != 2017) || (f(y).Text != Reformationstag(y).Text) {
			feiern = append(feiern, f)
		}
	}
	feiertermine := []Feiertag{}
	for _, f := range feiern {
		feiertermine = append(feiertermine, f(y))
	}
	sort.Sort(ByDate(feiertermine))
	return feiertermine
}

// BadenWürttemberg returns a Region object holding all public holidays in the state
// Baden-Württemberg
func BadenWürttemberg(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Epiphanias, Fronleichnam, Allerheiligen}
	return Region{"Baden-Württemberg", "BW", createFeiertagsList(y, ffun)}
}

// Bayern returns a Region object holding all public holidays in the state Bayern
func Bayern(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Epiphanias, Fronleichnam, Allerheiligen}
	return Region{"Bayern", "BY", createFeiertagsList(y, ffun)}
}

// Berlin returns a Region object holding all public holidays in the state Berlin
func Berlin(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Berlin", "BE", createFeiertagsList(y, ffun)}
}

// Brandenburg returns a Region object holding all public holidays in the state
// Brandenburg
func Brandenburg(y int, inklSonntage ...bool) Region {
	var ffun []func(int) Feiertag
	if len(inklSonntage) > 0 && inklSonntage[0] == false {
		ffun = []func(int) Feiertag{Reformationstag}
	} else {
		ffun = []func(int) Feiertag{Ostern, Pfingsten, Reformationstag}
	}
	return Region{"Brandenburg", "BB", createFeiertagsList(y, ffun)}
}

// Bremen returns a Region object holding all public holidays in the state Bremen
func Bremen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Bremen", "HB", createFeiertagsList(y, ffun)}
}

// Hamburg returns a Region object holding all public holidays in the state Hamburg
func Hamburg(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Hamburg", "HH", createFeiertagsList(y, ffun)}
}

// Hessen returns a Region object holding all public holidays in the state Hessen
func Hessen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Fronleichnam}
	return Region{"Hessen", "HE", createFeiertagsList(y, ffun)}
}

// MecklenburgVorpommern returns a Region object holding all public holidays in
// the state Mecklenburg-Vorpommern
func MecklenburgVorpommern(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Reformationstag}
	return Region{"Mecklenburg-Vorpommern", "MV", createFeiertagsList(y, ffun)}
}

// Niedersachsen returns a Region object holding all public holidays in the
// state Niedersachsen
func Niedersachsen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Niedersachsen", "NI", createFeiertagsList(y, ffun)}
}

// NordrheinWestfalen returns a Region object holding all public holidays in the
// state Nordrhein-Westfalen
func NordrheinWestfalen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Fronleichnam, Allerheiligen}
	return Region{"Nordrhein-Westfalen", "NW", createFeiertagsList(y, ffun)}
}

// RheinlandPfalz returns a Region object holding all public holidays in the
// state Rheinland-Pfalz
func RheinlandPfalz(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Fronleichnam, Allerheiligen}
	return Region{"Rheinland-Pfalz", "RP", createFeiertagsList(y, ffun)}
}

// Saarland returns a Region object holding all public holidays in the state Saarland
func Saarland(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Fronleichnam, MariäHimmelfahrt, Allerheiligen}
	return Region{"Saarland", "SL", createFeiertagsList(y, ffun)}
}

// Sachsen returns a Region object holding all public holidays in the state Sachsen
func Sachsen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Reformationstag, BußUndBettag}
	return Region{"Sachsen", "SN", createFeiertagsList(y, ffun)}
}

// SachsenAnhalt returns a Region object holding all public holidays in the state SachsenAnhalt
func SachsenAnhalt(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Epiphanias, Reformationstag}
	return Region{"Sachsen-Anhalt", "ST", createFeiertagsList(y, ffun)}
}

// SchleswigHolstein returns a Region object holding all public holidays in the state SchleswigHolstein
func SchleswigHolstein(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}
	return Region{"Schleswig-Holstein", "SH", createFeiertagsList(y, ffun)}
}

// Thüringen returns a Region object holding all public holidays in the state Thüringen
func Thüringen(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{
		Reformationstag}
	return Region{"Thüringen", "TH", createFeiertagsList(y, ffun)}
}

// Deutschland returns a Region object holding all public holidays that are Common in Germany
func Deutschland(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{}

	return Region{"Deutschland", "DE", createFeiertagsList(y, ffun)}
}

// All returns a Region object holding all public holidays/feast days known to this program.
// Not all of htem are public holidays (basically 'free').
func All(y int, inklSonntage ...bool) Region {
	ffun := []func(int) Feiertag{Epiphanias, HeiligeDreiKönige, Valentinstag,
		Weiberfastnacht, Rosenmontag, Fastnacht, Aschermittwoch, Gründonnerstag,
		BeginnSommerzeit, Walpurgisnacht, TagDerBefreiung, Muttertag, Vatertag, Fronleichnam,
		MariäHimmelfahrt, Reformationstag, Halloween, BeginnWinterzeit,
		Allerheiligen, Allerseelen, Martinstag, Karnevalsbeginn, BußUndBettag, Thanksgiving,
		Blackfriday, Volkstrauertag, Nikolaus, MariäUnbefleckteEmpfängnis, Heiligabend, Silvester}

	if len(inklSonntage) == 0 || inklSonntage[0] == true {
		ffun = append(ffun, Karnevalssonntag, Palmsonntag, Ostern, Pfingsten,
			Dreifaltigkeitssonntag, Erntedankfest, Totensonntag, ErsterAdvent, ZweiterAdvent,
			DritterAdvent, VierterAdvent)
	}

	return Region{"Alle", "All", createFeiertagsList(y, ffun)}
}
<|endoftext|>


<commit_before>package zoom

import (
	"errors"
	"fmt"
	"github.com/stephenalexbrowne/zoom/redis"
	"reflect"
)

// File contains code strictly having to do with relations. This includes
// saving relations in the database and converting the database representation
// back into the standard go struct form.

// iterate through relations and save each one
func saveRelations(in ModelInterface, val reflect.Value, ss *structSpec, name string, conn redis.Conn) error {
	for _, r := range ss.relations {

		// make sure we're dealing with a valid value
		relVal := val.Elem().FieldByName(r.fieldName)
		if !relVal.IsValid() {
			msg := fmt.Sprintf("zoom: Could not find field %s in %T. Value was %+v\n", r.fieldName, val.Elem().Interface(), val.Elem().Interface())
			return errors.New(msg)
		}
		if relVal.IsNil() {
			// skip empty relations
			continue
		}

		if r.typ == ONE_TO_ONE {

			// commit to database
			relModel, err := queueRelation(r, relVal, conn)
			if err != nil {
				return err
			}

			// add a command to the queue which will
			// add a relation key to the parent interface (in)
			key := name + ":" + in.GetId() + ":" + r.fieldName
			if err := conn.Send("set", key, relModel.GetId()); err != nil {
				// cancel transaction and return err
				conn.Do("discard")
				return err
			}

		} else if r.typ == ONE_TO_MANY {
			// iterate through the array and save each one
			for i := 0; i < relVal.Len(); i++ {
				relElem := relVal.Index(i)

				// commit to database
				relModel, err := queueRelation(r, relElem, conn)
				if err != nil {
					return err
				}

				// add a command to the queue which will
				// add a relation key to the parent interface (in)
				key := name + ":" + in.GetId() + ":" + r.fieldName
				if err := conn.Send("sadd", key, relModel.GetId()); err != nil {
					// cancel transaction and return err
					conn.Do("discard")
					return err
				}
			}
		}
	}
	return nil
}

// adds a command to the queue to add the relational struct to database and
// returns a ModelInterface that can be used
func queueRelation(r *relation, relVal reflect.Value, conn redis.Conn) (ModelInterface, error) {
	// cast to a ModelInterface
	relModel, ok := relVal.Interface().(ModelInterface)
	if !ok {
		msg := fmt.Sprintf("The type %T does not implement ModelInterface. Does it have a *zoom.Model embedded field?", relVal.Interface())
		return nil, errors.New(msg)
	}

	// assign an id if needed
	if relModel.GetId() == "" {
		relModel.SetId(generateRandomId())
	}

	// format the args
	key := r.redisName + ":" + relModel.GetId()
	args := Args{}.Add(key)
	args = append(args, argsForSpec(relVal.Elem(), r.spec)...)

	// add a command to the queue which will
	// add the struct to the database as a redis hash
	if err := conn.Send("hmset", args...); err != nil {
		// cancel transaction and return err
		conn.Do("discard")
		return nil, err
	}

	// add to the index for this model
	if err := queueAddToIndex(r.redisName, relModel.GetId(), conn); err != nil {
		return nil, err
	}

	return relModel, nil
}

func scanRelations(ss *structSpec, modelName string, modelId string, modelVal reflect.Value, conn redis.Conn) error {
	// iterate through each relation in the spec
	for _, r := range ss.relations {

		// construct the key for the related model
		key := modelName + ":" + modelId + ":" + r.fieldName

		// make sure the key exists
		exists, err := KeyExists(key, conn)
		if err != nil {
			return err
		}
		if exists {
			if r.typ == ONE_TO_ONE {
				if err := scanOneToOneRelation(r, modelVal, key, conn); err != nil {
					return err
				}
			} else if r.typ == ONE_TO_MANY {
				if err := scanOneToManyRelation(r, modelVal, key, conn); err != nil {
					return err
				}
			}
		} // if it doesn't exist, we do nothing
	}
	return nil
}

func scanOneToOneRelation(r *relation, modelVal reflect.Value, key string, conn redis.Conn) error {
	// get the id of the relation from redis
	relId, err := redis.String(conn.Do("get", key))
	if err != nil {
		return err
	}

	// get the settable field
	relField := modelVal.Elem().FieldByName(r.fieldName)

	// make sure we can set relField
	if !relField.CanSet() {
		msg := fmt.Sprintf("zoom: couldn't set field: %+v\n", relField)
		return errors.New(msg)
	}

	// see if the relation has been saved in redis
	// if not we should just leave it as nil
	key = r.redisName + ":" + relId
	exists, err := KeyExists(key, conn)
	if err != nil {
		return err
	}
	if !exists {
		return nil
	}

	// get the stuff from redis
	bulk, err := redis.MultiBulk(conn.Do("hgetall", key))
	if err != nil {
		return err
	}

	// create a new struct and instantiate its Model attribute
	// this gives us the embedded methods and properties on Model
	var relVal reflect.Value
	relVal = reflect.New(relField.Type().Elem())
	relVal.Elem().FieldByName("Model").Set(reflect.ValueOf(new(Model)))

	// type assert to ModelInterface so we can use SetId()
	relModel := relVal.Interface().(ModelInterface)

	// invoke redis driver to fill in the values of the struct
	err = ScanStruct(bulk, relModel)
	if err != nil {
		return err
	}

	// set the id
	relModel.SetId(relId)

	// set relField to the value of the new struct we just created
	relField.Set(relVal)

	return nil
}

func scanOneToManyRelation(r *relation, modelVal reflect.Value, key string, conn redis.Conn) error {
	// invoke redis driver to get a set of keys
	relIds, err := redis.Strings(conn.Do("smembers", key))
	if err != nil {
		return err
	}

	// setup a transaction
	if err := conn.Send("multi"); err != nil {
		return err
	}

	validIds := make([]string, 0)

	// We'll pipeline this by first executing all the redis queries,
	// then dealing with each response.
	for _, relId := range relIds {
		validId, err := queueOneToManyRelation(r, relId, conn)
		if err != nil {
			return err
		}
		// if validId is not blank, we take it to mean that the command was added
		// and the model with the id validId is going to be read and scanned
		if validId != "" {
			validIds = append(validIds, validId)
		}
	}

	// call EXEC to execute all the commands in the transaction queue
	replies, err := redis.MultiBulk(conn.Do("exec"))
	if err != nil {
		return err
	}

	// now iterate through each reply and create structs, then append them to the slice
	for _, reply := range replies {

		// convert each reply to a slice
		bulk, err := redis.MultiBulk(reply, nil)
		if err != nil {
			return err
		}

		// create a settable slice
		settable := modelVal.Elem().FieldByName(r.fieldName)

		// create a new struct and instantiate its Model attribute
		// this gives us the embedded methods and properties on Model
		relVal := reflect.New(settable.Type().Elem().Elem())
		relVal.Elem().FieldByName("Model").Set(reflect.ValueOf(new(Model)))

		// type assert to ModelInterface so we can use SetId()
		relModel := relVal.Interface().(ModelInterface)

		// fill in the values of the struct
		err = ScanStruct(bulk, relModel)
		if err != nil {
			return err
		}

		// set the id
		id := validIds[0]
		validIds = validIds[1:]
		relModel.SetId(id)

		// append to the slice of related structs
		sliceVal := reflect.Append(settable, relVal)
		settable.Set(sliceVal)
	}

	return nil
}

// returns an id if the command was successfully added to the queue. If the key didn't exist,
// then it returns an empty string but not an error
func queueOneToManyRelation(r *relation, relId string, conn redis.Conn) (string, error) {

	// see if the relation has been saved in redis
	// if not we should just leave it as nil
	key := r.redisName + ":" + relId
	// NOTE: here we're passing in nil, so the function will create
	// and close a new connection for us. This prevents ruining the transaction
	// going on in our current conn.
	exists, err := KeyExists(key, nil)
	if err != nil {
		return "", err
	}
	if !exists {
		return "", nil
	}

	// add the command to the queue
	if err := conn.Send("hgetall", key); err != nil {
		return "", err
	}

	return relId, nil
}
<commit_msg>Remove old files<commit_after><|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Extended and bugfixes by Miek Gieben. Copyright 2010-2012.

// DOMAIN NAME SYSTEM
//
// Package dns implements a full featured interface to the Domain Name System.
// The package allows complete control over what is send out to the DNS. The package
// API follows the less-is-more principle, by presenting a small, clean interface.
//
// Resource records are native types. They are not stored in wire format.
// Basic usage pattern for creating a new resource record:
//
//      r := new(RR_TXT)
//      r.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeMX, Class: ClassINET, Ttl: 3600}
//      r.Pref = 10
//      r.Mx = "mx.miek.nl."
//
// Or directly from a string:
//
//      mx, err := NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
//
// Or when the default TTL (3600) and class (IN) suit you:
//
//      mx, err := NewRR("miek.nl. MX 10 mx.miek.nl.")
//
// Or even:
//
//      mx, err := NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
//
// The package dns supports (async) querying/replying, incoming/outgoing Axfr/Ixfr,
// TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
// Note that domain names MUST be full qualified, before sending them. The packages
// enforces this, by throwing a panic().
//
// In the DNS messages are exchanged. Use pattern for creating one:
//
//      m := new(Msg)
//      m.SetQuestion("miek.nl.", TypeMX)
//
// The message m is now a message with the question section set to ask
// the MX records for the miek.nl. zone.
//
// The following is slightly more verbose, but more flexible:
//
//      m1 := new(Msg)
//      m1.MsgHdr.Id = Id()
//      m1.MsgHdr.RecursionDesired = false
//      m1.Question = make([]Question, 1)
//      m1.Question[0] = Question{"miek.nl.", TypeMX, ClassINET}
//
// After creating a message it can be send.
// Basic use pattern for synchronous querying the DNS at a
// server configured on 127.0.0.1 and port 53:
//
//      c := NewClient()
//      in := c.Exchange(m1, "127.0.0.1:53")
//
// An asynchronous query is also possible, setting up is more elaborate then
// a synchronous query. The Basic use pattern is:
//
//      HandleQueryFunc(".", handler)
//      ListenAndQuery(nil, nil)
//      c.Do(m1, "127.0.0.1:53")
//      // Do something else
//      r := <- DefaultReplyChan
//      // r.Reply is the answer
//      // r.Request is the original request
//      // r.Error is the error (if any)
package dns

import (
	"net"
	"strconv"
)

const (
	Year68         = 1 << 32 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
	DefaultMsgSize = 4096    // Standard default for larger than 512 packets.
	UDPMsgSize     = 512     // Default buffer size for servers receiving UDP packets.
	MaxMsgSize     = 65536   // Largest possible DNS packet.
	DefaultTtl     = 3600    // Default TTL.
)

// Error represents a DNS error
type Error struct {
	Err     string
	Name    string
	Server  net.Addr
	Timeout bool
}

func (e *Error) Error() string {
	if e == nil {
		return "<nil>"
	}
	if e.Name == "" {
		return e.Err
	}
	return e.Name + ": " + e.Err

}

// An RR represents a resource record.
type RR interface {
	// Header returns the header of an resource record. The header contains
	// everything up to the rdata.
	Header() *RR_Header
	// String returns the text representation of the resource record.
	String() string
	// Len returns the length (in octects) of the uncompressed RR in wire format.
	Len() int
}

// Exchange is used in communicating with the resolver.
type Exchange struct {
	Request *Msg  // the question sent
	Reply   *Msg  // the answer to the question that was sent
	Error   error // if something went wrong, this contains the error
}

// DNS resource records.
// There are many types of messages,
// but they all share the same header.
type RR_Header struct {
	Name     string "cdomain-name"
	Rrtype   uint16
	Class    uint16
	Ttl      uint32
	Rdlength uint16 // length of data after header
}

func (h *RR_Header) Header() *RR_Header {
	return h
}

func (h *RR_Header) String() string {
	var s string

	if h.Rrtype == TypeOPT {
		s = ";"
		// and maybe other things
	}

	if len(h.Name) == 0 {
		s += ".\t"
	} else {
		s += h.Name + "\t"
	}
	s = s + strconv.FormatInt(int64(h.Ttl), 10) + "\t"

	if _, ok := Class_str[h.Class]; ok {
		s += Class_str[h.Class] + "\t"
	} else {
		s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t"
	}

	if _, ok := Rr_str[h.Rrtype]; ok {
		s += Rr_str[h.Rrtype] + "\t"
	} else {
		s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t"
	}
	return s
}

func (h *RR_Header) Len() int {
	l := len(h.Name) + 1
	l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
	return l
}

// Create a copy of the header

func (h *RR_Header) Copy() *RR_Header {
	h1 := new(RR_Header)
	h1.Name = h.Name
	h1.Rrtype = h.Rrtype
	h1.Class = h.Class
	h1.Ttl = h.Ttl
	h1.Rdlength = h.Rdlength
	return h1
}

func zoneMatch(pattern, zone string) (ok bool) {
	if len(pattern) == 0 {
		return
	}
	if len(zone) == 0 {
		zone = "."
	}
	pattern = Fqdn(pattern)
	zone = Fqdn(zone)
	i := 0
	for {
		ok = pattern[len(pattern)-1-i] == zone[len(zone)-1-i]
		i++

		if !ok {
			break
		}
		if len(pattern)-1-i < 0 || len(zone)-1-i < 0 {
			break
		}

	}
	return
}
<commit_msg>small doc update<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Extended and bugfixes by Miek Gieben. Copyright 2010-2012.

// DOMAIN NAME SYSTEM
//
// Package dns implements a full featured interface to the Domain Name System.
// The package allows complete control over what is send out to the DNS. The package
// API follows the less-is-more principle, by presenting a small, clean interface.
//
// Resource records are native types. They are not stored in wire format.
// Basic usage pattern for creating a new resource record:
//
//      r := new(RR_TXT)
//      r.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeMX, Class: ClassINET, Ttl: 3600}
//      r.Pref = 10
//      r.Mx = "mx.miek.nl."
//
// Or directly from a string:
//
//      mx, err := NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
//
// Or when the default TTL (3600) and class (IN) suit you:
//
//      mx, err := NewRR("miek.nl. MX 10 mx.miek.nl.")
//
// Or even:
//
//      mx, err := NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
//
// The package dns supports (async) querying/replying, incoming/outgoing Axfr/Ixfr,
// TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
// Note that domain names MUST be full qualified, before sending them. The packages
// enforces this, by throwing a panic().
//
// In the DNS messages are exchanged. Use pattern for creating one:
//
//      m := new(Msg)
//      m.SetQuestion("miek.nl.", TypeMX)
//
// The message m is now a message with the question section set to ask
// the MX records for the miek.nl. zone.
//
// The following is slightly more verbose, but more flexible:
//
//      m1 := new(Msg)
//      m1.MsgHdr.Id = Id()
//      m1.MsgHdr.RecursionDesired = false
//      m1.Question = make([]Question, 1)
//      m1.Question[0] = Question{"miek.nl.", TypeMX, ClassINET}
//
// After creating a message it can be send.
// Basic use pattern for synchronous querying the DNS at a
// server configured on 127.0.0.1 and port 53:
//
//      c := NewClient()
//      in := c.Exchange(m1, "127.0.0.1:53")
//
// An asynchronous query is also possible, setting up is more elaborate then
// a synchronous query. The Basic use pattern is:
//
//      HandleQuery(".", handler)
//      ListenAndQuery(nil, nil)
//      c.Do(m1, "127.0.0.1:53")
//      // Do something else
//      r := <- DefaultReplyChan
//      // r.Reply is the answer
//      // r.Request is the original request
//      // r.Error is the error (if any)
package dns

import (
	"net"
	"strconv"
)

const (
	Year68         = 1 << 32 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
	DefaultMsgSize = 4096    // Standard default for larger than 512 packets.
	UDPMsgSize     = 512     // Default buffer size for servers receiving UDP packets.
	MaxMsgSize     = 65536   // Largest possible DNS packet.
	DefaultTtl     = 3600    // Default TTL.
)

// Error represents a DNS error
type Error struct {
	Err     string
	Name    string
	Server  net.Addr
	Timeout bool
}

func (e *Error) Error() string {
	if e == nil {
		return "<nil>"
	}
	if e.Name == "" {
		return e.Err
	}
	return e.Name + ": " + e.Err

}

// An RR represents a resource record.
type RR interface {
	// Header returns the header of an resource record. The header contains
	// everything up to the rdata.
	Header() *RR_Header
	// String returns the text representation of the resource record.
	String() string
	// Len returns the length (in octects) of the uncompressed RR in wire format.
	Len() int
}

// Exchange is used in communicating with the resolver.
type Exchange struct {
	Request *Msg  // the question sent
	Reply   *Msg  // the answer to the question that was sent
	Error   error // if something went wrong, this contains the error
}

// DNS resource records.
// There are many types of messages,
// but they all share the same header.
type RR_Header struct {
	Name     string "cdomain-name"
	Rrtype   uint16
	Class    uint16
	Ttl      uint32
	Rdlength uint16 // length of data after header
}

func (h *RR_Header) Header() *RR_Header {
	return h
}

func (h *RR_Header) String() string {
	var s string

	if h.Rrtype == TypeOPT {
		s = ";"
		// and maybe other things
	}

	if len(h.Name) == 0 {
		s += ".\t"
	} else {
		s += h.Name + "\t"
	}
	s = s + strconv.FormatInt(int64(h.Ttl), 10) + "\t"

	if _, ok := Class_str[h.Class]; ok {
		s += Class_str[h.Class] + "\t"
	} else {
		s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t"
	}

	if _, ok := Rr_str[h.Rrtype]; ok {
		s += Rr_str[h.Rrtype] + "\t"
	} else {
		s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t"
	}
	return s
}

func (h *RR_Header) Len() int {
	l := len(h.Name) + 1
	l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
	return l
}

// Create a copy of the header

func (h *RR_Header) Copy() *RR_Header {
	h1 := new(RR_Header)
	h1.Name = h.Name
	h1.Rrtype = h.Rrtype
	h1.Class = h.Class
	h1.Ttl = h.Ttl
	h1.Rdlength = h.Rdlength
	return h1
}

func zoneMatch(pattern, zone string) (ok bool) {
	if len(pattern) == 0 {
		return
	}
	if len(zone) == 0 {
		zone = "."
	}
	pattern = Fqdn(pattern)
	zone = Fqdn(zone)
	i := 0
	for {
		ok = pattern[len(pattern)-1-i] == zone[len(zone)-1-i]
		i++

		if !ok {
			break
		}
		if len(pattern)-1-i < 0 || len(zone)-1-i < 0 {
			break
		}

	}
	return
}
<|endoftext|>