<commit_before>// Copyright 2016 The Serviced 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 service

import (
	"path"
	"sync"
	"time"

	"github.com/control-center/serviced/coordinator/client"

	log "github.com/Sirupsen/logrus"
	"github.com/control-center/serviced/domain/service"
)

// HostStateHandler is the handler for running the HostListener
type HostStateHandler interface {

	// StopsContainer stops the container if the container exists and isn't
	// already stopped.
	StopContainer(serviceID string, instanceID int) error

	// AttachContainer attaches to an existing container for the service
	// instance. Returns nil channel if the container id doesn't match or if
	// the container has stopped. Channel reports the time that the container
	// has stopped.
	AttachContainer(state *ServiceState, serviceID string, instanceID int) (<-chan time.Time, error)

	// StartContainer creates and starts a new container for the given service
	// instance.  It returns relevant information about the container and a
	// channel that triggers when the container has stopped.
	StartContainer(cancel <-chan interface{}, serviceID string, instanceID int) (*ServiceState, <-chan time.Time, error)

	// ResumeContainer resumes a paused container.  Returns nil if the
	// container has stopped or if it doesn't exist.
	ResumeContainer(serviceID string, instanceID int) error

	// PauseContainer pauses a running container.  Returns nil if the container
	// has stopped or if it doesn't exist.
	PauseContainer(serviceID string, instanceID int) error
}

type hostStateThread struct {
	data   *ServiceState
	exited <-chan time.Time
}

// HostStateListener is the listener for monitoring service instances
type HostStateListener struct {
	conn     client.Connection
	handler  HostStateHandler
	hostID   string
	shutdown <-chan interface{}
	mu       *sync.RWMutex
	threads  map[string]hostStateThread
}

// NewHostStateListener instantiates a HostStateListener object
func NewHostStateListener(handler HostStateHandler, hostID string, shutdown <-chan interface{}) *HostStateListener {
	l := &HostStateListener{
		handler:  handler,
		hostID:   hostID,
		shutdown: shutdown,
		mu:       &sync.RWMutex{},
		threads:  make(map[string]hostStateThread),
	}
	go l.watchForShutdown()
	return l
}

// GetConnection implements zzk.Listener
func (l *HostStateListener) SetConnection(conn client.Connection) { l.conn = conn }

// GetPath implements zzk.Listener
func (l *HostStateListener) GetPath(nodes ...string) string {
	parts := append([]string{"/hosts", l.hostID, "instances"}, nodes...)
	return path.Join(parts...)
}

// Ready implements zzk.Listener
func (l *HostStateListener) Ready() error {
	return nil
}

// Done implements zzk.Listener
func (l *HostStateListener) Done() {
}

// PostProcess implements zzk.Listener
// This is always called after all threads have been spawned
func (l *HostStateListener) PostProcess(p map[string]struct{}) {
	// We are running all of the containers we are supposed to, now
	// shut down any containers we are not supposed to be running
	l.mu.Lock()
	defer l.mu.Unlock()
	stateIDs := l.getExistingStateIDs()
	for _, s := range stateIDs {
		if _, ok := p[s]; !ok {
			l.cleanUpContainer(s)
		}
	}
}

// Spawn listens for changes in the host state and manages running instances
func (l *HostStateListener) Spawn(cancel <-chan interface{}, stateID string) {
	logger := plog.WithFields(log.Fields{
		"hostid":  l.hostID,
		"stateid": stateID,
	})

	// If we are shutting down, just return
	select {
	case <-l.shutdown:
		logger.Debug("Will not spawn, shutting down")
		return
	default:
	}

	// check if the state id is valid
	hostID, serviceID, instanceID, err := ParseStateID(stateID)
	if err != nil || hostID != l.hostID {

		logger.WithField("hostidmatch", hostID == l.hostID).WithError(err).Warn("Invalid state id, deleting")

		// clean up the bad node
		if err := l.conn.Delete(l.GetPath(stateID)); err != nil && err != client.ErrNoNode {
			logger.WithError(err).Error("Could not delete host state")
		}
		return
	}

	logger = logger.WithFields(log.Fields{
		"serviceid":  serviceID,
		"instanceid": instanceID,
	})

	// set up the request object for updates
	req := StateRequest{
		PoolID:     "",
		HostID:     hostID,
		ServiceID:  serviceID,
		InstanceID: instanceID,
	}

	// reattach to orphaned container
	l.mu.RLock()
	ssdat, containerExit := l.getExistingState(stateID)
	l.mu.RUnlock()

	sspth := path.Join("/services", serviceID, stateID)
	// load the service state node
	if ssdat == nil {
		ssdat = &ServiceState{}
		if err := l.conn.Get(sspth, ssdat); err == client.ErrNoNode {
			l.mu.Lock()
			l.cleanUpContainer(stateID)
			l.mu.Unlock()
			return
		} else if err != nil {
			logger.WithError(err).Error("Could not load service state")
			return
		}
	}

	done := make(chan struct{})
	defer func() { close(done) }()

	for {
		// set up a listener on the host state node
		hspth := l.GetPath(stateID)
		hsdat := &HostState{}
		hsevt, err := l.conn.GetW(hspth, hsdat, done)
		if err == client.ErrNoNode {
			logger.Debug("Host state was removed, exiting")
			l.mu.Lock()
			l.cleanUpContainer(stateID)
			l.mu.Unlock()
			return
		} else if err != nil {
			logger.WithError(err).Error("Could not watch host state")
			return
		}

		// set up a listener on the service state node, to ensure the node's existence
		ok, ssevt, err := l.conn.ExistsW(sspth, done)
		if err != nil {
			logger.WithError(err).Error("Could not watch service state")
			return
		} else if !ok {
			logger.Debug("Service state was removed, exiting")
			l.mu.Lock()
			l.cleanUpContainer(stateID)
			l.mu.Unlock()
			return
		}

		// set the state of this instance
		setInstanceState := func() bool {
			l.mu.Lock()
			defer l.mu.Unlock()

			// Don't do anything if we are shutting down
			select {
			case <-l.shutdown:
				return false
			default:
			}
			// attach to the container if not already attached
			if containerExit == nil {
				containerExit, err = l.handler.AttachContainer(ssdat, serviceID, instanceID)
				if err != nil {
					logger.WithError(err).Error("Could not attach to container")
					l.cleanUpContainer(stateID)
					return false
				}
				l.setExistingState(stateID, ssdat, containerExit)
			}

			switch hsdat.DesiredState {
			case service.SVCRun:
				if containerExit == nil {
					// container is detached because it doesn't exist
					ssdat, containerExit, err = l.handler.StartContainer(l.shutdown, serviceID, instanceID)
					if err != nil {
						logger.WithError(err).Error("Could not start container")
						l.cleanUpContainer(stateID)
						return false
					}

					// set the service state
					l.setExistingState(stateID, ssdat, containerExit)
					logger.Debug("Started container")

					if err := l.updateServiceStateInZK(ssdat, req); err != nil {
						logger.WithError(err).Error("Could not set state for started container")
						return false
					}

					return true
				} else if ssdat.Paused {
					// resume paused container
					if err := l.handler.ResumeContainer(serviceID, instanceID); err != nil {
						logger.WithError(err).Error("Could not resume container")
						l.cleanUpContainer(stateID)
						return false
					}

					// update the service state
					ssdat.Paused = false
					l.setExistingState(stateID, ssdat, containerExit)

					if err := l.updateServiceStateInZK(ssdat, req); err != nil {
						logger.WithError(err).Error("Could not set state for resumed container")
						return false
					}

					logger.Debug("Resumed paused container")
				}
			case service.SVCPause:
				if containerExit != nil && !ssdat.Paused {
					// container is attached and not paused, so pause the container
					if err := l.handler.PauseContainer(serviceID, instanceID); err != nil {
						logger.WithError(err).Error("Could not pause container")
						l.cleanUpContainer(stateID)
						return false
					}

					// update the service state
					ssdat.Paused = true
					l.setExistingState(stateID, ssdat, containerExit)
					if err := l.updateServiceStateInZK(ssdat, req); err != nil {
						logger.WithError(err).Error("Could not set state for resumed container")
						return false
					}

					logger.Debug("Paused running container")
				}
			case service.SVCStop:
				// shut down the container and clean up nodes
				l.cleanUpContainer(stateID)
				return false
			default:
				logger.Debug("Could not process desired state for instance")
			}
			return true
		}

		if !setInstanceState() {
			return
		}

		logger.Debug("Waiting for event on host state")
		select {
		case <-hsevt:
		case <-ssevt:
		case timeExit := <-containerExit:
			handleContainerExit := func() bool {
				l.mu.Lock()
				defer l.mu.Unlock()

				// Don't do anything if we are shutting down, the shutdown cleanup will handle it
				select {
				case <-l.shutdown:
					return false
				default:
				}

				// set the service state
				ssdat.Terminated = timeExit
				containerExit = nil
				l.setExistingState(stateID, ssdat, containerExit)
				logger.WithField("terminated", timeExit).Warn("Container exited unexpectedly, restarting")

				if err := l.updateServiceStateInZK(ssdat, req); err != nil {
					logger.WithError(err).Error("Could not set state for stopped container")
					// TODO: we currently don't support containers restarting if
					// shut down during an outage, so don't bother
					return false
				}
				return true
			}
			if !handleContainerExit() {
				return
			}
		case <-cancel:
			logger.Debug("Host state listener received signal to cancel listening")
			return
		case <-l.shutdown:
			logger.Debug("Host state listener received signal to shutdown")
			// Container shutdown will be handled by the HostStateListener for all containers during shutdown
			return
		}

		close(done)
		done = make(chan struct{})
	}
}

// Gets a list of state IDs for all existing threads
//  Call l.mu.RLock() first
func (l *HostStateListener) getExistingStateIDs() []string {
	stateIds := make([]string, len(l.threads))
	i := 0
	for s := range l.threads {
		stateIds[i] = s
		i++
	}
	return stateIds
}

// Gets the ServiceState for an existing thread
//  Call l.mu.RLock() first
func (l *HostStateListener) getExistingState(stateID string) (*ServiceState, <-chan time.Time) {
	if thread, ok := l.threads[stateID]; ok {
		return thread.data, thread.exited
	}
	return nil, nil
}

// Adds a state to the internal thread list.
//  Call l.mu.Lock() first
func (l *HostStateListener) setExistingState(stateID string, data *ServiceState, containerExit <-chan time.Time) {
	l.threads[stateID] = hostStateThread{data, containerExit}
}

// Removes a state from the internal thread list
//  Call l.mu.Lock() first
func (l *HostStateListener) removeExistingState(stateID string) {
	delete(l.threads, stateID)
}

func (l *HostStateListener) updateServiceStateInZK(data *ServiceState, req StateRequest) error {
	return UpdateState(l.conn, req, func(s *State) bool {
		s.ServiceState = *data
		return true
	})
}

// Stops the running container, cleans up zk nodes, and removes the thread from the thread list
//  Call l.mu.Lock() first.
func (l *HostStateListener) cleanUpContainer(stateID string) {
	logger := plog.WithFields(log.Fields{
		"hostid":  l.hostID,
		"stateid": stateID,
	})

	// Parse the stateID
	hostID, serviceID, instanceID, err := ParseStateID(stateID)
	if err != nil || hostID != l.hostID {
		logger.WithField("hostidmatch", hostID == l.hostID).WithError(err).Warn("Could not clean up container: Invalid state id")
		return
	}

	logger = logger.WithFields(log.Fields{
		"serviceid":  serviceID,
		"instanceid": instanceID,
	})

	// Get the containerExit channel from our thread map
	_, containerExit := l.getExistingState(stateID)

	// stop the container
	if err := l.handler.StopContainer(serviceID, instanceID); err != nil {
		logger.WithError(err).Error("Could not stop container")
	} else if containerExit != nil {
		// wait for the container to exit
		time := <-containerExit
		logger.WithField("terminated", time).Debug("Container exited")
	}

	// Remove the container from our thread map
	l.removeExistingState(stateID)

	// delete the state from the coordinator
	req := StateRequest{
		PoolID:     "",
		HostID:     hostID,
		ServiceID:  serviceID,
		InstanceID: instanceID,
	}
	if err := DeleteState(l.conn, req); err != nil {
		logger.WithError(err).Warn("Could not delete state for stopped container")
		return
	}
}

func (l *HostStateListener) watchForShutdown() {
	<-l.shutdown
	l.mu.Lock()
	defer l.mu.Unlock()
	stateIDs := l.getExistingStateIDs()
	for _, s := range stateIDs {
		l.cleanUpContainer(s)
	}
}
<commit_msg>Replace hostStateThread with explicit struct<commit_after>// Copyright 2016 The Serviced 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 service

import (
	"path"
	"sync"
	"time"

	"github.com/control-center/serviced/coordinator/client"

	log "github.com/Sirupsen/logrus"
	"github.com/control-center/serviced/domain/service"
)

// HostStateHandler is the handler for running the HostListener
type HostStateHandler interface {

	// StopsContainer stops the container if the container exists and isn't
	// already stopped.
	StopContainer(serviceID string, instanceID int) error

	// AttachContainer attaches to an existing container for the service
	// instance. Returns nil channel if the container id doesn't match or if
	// the container has stopped. Channel reports the time that the container
	// has stopped.
	AttachContainer(state *ServiceState, serviceID string, instanceID int) (<-chan time.Time, error)

	// StartContainer creates and starts a new container for the given service
	// instance.  It returns relevant information about the container and a
	// channel that triggers when the container has stopped.
	StartContainer(cancel <-chan interface{}, serviceID string, instanceID int) (*ServiceState, <-chan time.Time, error)

	// ResumeContainer resumes a paused container.  Returns nil if the
	// container has stopped or if it doesn't exist.
	ResumeContainer(serviceID string, instanceID int) error

	// PauseContainer pauses a running container.  Returns nil if the container
	// has stopped or if it doesn't exist.
	PauseContainer(serviceID string, instanceID int) error
}

// HostStateListener is the listener for monitoring service instances
type HostStateListener struct {
	conn     client.Connection
	handler  HostStateHandler
	hostID   string
	shutdown <-chan interface{}
	mu       *sync.RWMutex
	threads  map[string]struct {
		data   *ServiceState
		exited <-chan time.Time
	}
}

// NewHostStateListener instantiates a HostStateListener object
func NewHostStateListener(handler HostStateHandler, hostID string, shutdown <-chan interface{}) *HostStateListener {
	l := &HostStateListener{
		handler:  handler,
		hostID:   hostID,
		shutdown: shutdown,
		mu:       &sync.RWMutex{},
		threads: make(map[string]struct {
			data   *ServiceState
			exited <-chan time.Time
		}),
	}
	go l.watchForShutdown()
	return l
}

// GetConnection implements zzk.Listener
func (l *HostStateListener) SetConnection(conn client.Connection) { l.conn = conn }

// GetPath implements zzk.Listener
func (l *HostStateListener) GetPath(nodes ...string) string {
	parts := append([]string{"/hosts", l.hostID, "instances"}, nodes...)
	return path.Join(parts...)
}

// Ready implements zzk.Listener
func (l *HostStateListener) Ready() error {
	return nil
}

// Done implements zzk.Listener
func (l *HostStateListener) Done() {
}

// PostProcess implements zzk.Listener
// This is always called after all threads have been spawned
func (l *HostStateListener) PostProcess(p map[string]struct{}) {
	// We are running all of the containers we are supposed to, now
	// shut down any containers we are not supposed to be running
	l.mu.Lock()
	defer l.mu.Unlock()
	stateIDs := l.getExistingStateIDs()
	for _, s := range stateIDs {
		if _, ok := p[s]; !ok {
			l.cleanUpContainer(s)
		}
	}
}

// Spawn listens for changes in the host state and manages running instances
func (l *HostStateListener) Spawn(cancel <-chan interface{}, stateID string) {
	logger := plog.WithFields(log.Fields{
		"hostid":  l.hostID,
		"stateid": stateID,
	})

	// If we are shutting down, just return
	select {
	case <-l.shutdown:
		logger.Debug("Will not spawn, shutting down")
		return
	default:
	}

	// check if the state id is valid
	hostID, serviceID, instanceID, err := ParseStateID(stateID)
	if err != nil || hostID != l.hostID {

		logger.WithField("hostidmatch", hostID == l.hostID).WithError(err).Warn("Invalid state id, deleting")

		// clean up the bad node
		if err := l.conn.Delete(l.GetPath(stateID)); err != nil && err != client.ErrNoNode {
			logger.WithError(err).Error("Could not delete host state")
		}
		return
	}

	logger = logger.WithFields(log.Fields{
		"serviceid":  serviceID,
		"instanceid": instanceID,
	})

	// set up the request object for updates
	req := StateRequest{
		PoolID:     "",
		HostID:     hostID,
		ServiceID:  serviceID,
		InstanceID: instanceID,
	}

	// reattach to orphaned container
	l.mu.RLock()
	ssdat, containerExit := l.getExistingState(stateID)
	l.mu.RUnlock()

	sspth := path.Join("/services", serviceID, stateID)
	// load the service state node
	if ssdat == nil {
		ssdat = &ServiceState{}
		if err := l.conn.Get(sspth, ssdat); err == client.ErrNoNode {
			l.mu.Lock()
			l.cleanUpContainer(stateID)
			l.mu.Unlock()
			return
		} else if err != nil {
			logger.WithError(err).Error("Could not load service state")
			return
		}
	}

	done := make(chan struct{})
	defer func() { close(done) }()

	for {
		// set up a listener on the host state node
		hspth := l.GetPath(stateID)
		hsdat := &HostState{}
		hsevt, err := l.conn.GetW(hspth, hsdat, done)
		if err == client.ErrNoNode {
			logger.Debug("Host state was removed, exiting")
			l.mu.Lock()
			l.cleanUpContainer(stateID)
			l.mu.Unlock()
			return
		} else if err != nil {
			logger.WithError(err).Error("Could not watch host state")
			return
		}

		// set up a listener on the service state node, to ensure the node's existence
		ok, ssevt, err := l.conn.ExistsW(sspth, done)
		if err != nil {
			logger.WithError(err).Error("Could not watch service state")
			return
		} else if !ok {
			logger.Debug("Service state was removed, exiting")
			l.mu.Lock()
			l.cleanUpContainer(stateID)
			l.mu.Unlock()
			return
		}

		// set the state of this instance
		setInstanceState := func() bool {
			l.mu.Lock()
			defer l.mu.Unlock()

			// Don't do anything if we are shutting down
			select {
			case <-l.shutdown:
				return false
			default:
			}
			// attach to the container if not already attached
			if containerExit == nil {
				containerExit, err = l.handler.AttachContainer(ssdat, serviceID, instanceID)
				if err != nil {
					logger.WithError(err).Error("Could not attach to container")
					l.cleanUpContainer(stateID)
					return false
				}
				l.setExistingState(stateID, ssdat, containerExit)
			}

			switch hsdat.DesiredState {
			case service.SVCRun:
				if containerExit == nil {
					// container is detached because it doesn't exist
					ssdat, containerExit, err = l.handler.StartContainer(l.shutdown, serviceID, instanceID)
					if err != nil {
						logger.WithError(err).Error("Could not start container")
						l.cleanUpContainer(stateID)
						return false
					}

					// set the service state
					l.setExistingState(stateID, ssdat, containerExit)
					logger.Debug("Started container")

					if err := l.updateServiceStateInZK(ssdat, req); err != nil {
						logger.WithError(err).Error("Could not set state for started container")
						return false
					}

					return true
				} else if ssdat.Paused {
					// resume paused container
					if err := l.handler.ResumeContainer(serviceID, instanceID); err != nil {
						logger.WithError(err).Error("Could not resume container")
						l.cleanUpContainer(stateID)
						return false
					}

					// update the service state
					ssdat.Paused = false
					l.setExistingState(stateID, ssdat, containerExit)

					if err := l.updateServiceStateInZK(ssdat, req); err != nil {
						logger.WithError(err).Error("Could not set state for resumed container")
						return false
					}

					logger.Debug("Resumed paused container")
				}
			case service.SVCPause:
				if containerExit != nil && !ssdat.Paused {
					// container is attached and not paused, so pause the container
					if err := l.handler.PauseContainer(serviceID, instanceID); err != nil {
						logger.WithError(err).Error("Could not pause container")
						l.cleanUpContainer(stateID)
						return false
					}

					// update the service state
					ssdat.Paused = true
					l.setExistingState(stateID, ssdat, containerExit)
					if err := l.updateServiceStateInZK(ssdat, req); err != nil {
						logger.WithError(err).Error("Could not set state for resumed container")
						return false
					}

					logger.Debug("Paused running container")
				}
			case service.SVCStop:
				// shut down the container and clean up nodes
				l.cleanUpContainer(stateID)
				return false
			default:
				logger.Debug("Could not process desired state for instance")
			}
			return true
		}

		if !setInstanceState() {
			return
		}

		logger.Debug("Waiting for event on host state")
		select {
		case <-hsevt:
		case <-ssevt:
		case timeExit := <-containerExit:
			handleContainerExit := func() bool {
				l.mu.Lock()
				defer l.mu.Unlock()

				// Don't do anything if we are shutting down, the shutdown cleanup will handle it
				select {
				case <-l.shutdown:
					return false
				default:
				}

				// set the service state
				ssdat.Terminated = timeExit
				containerExit = nil
				l.setExistingState(stateID, ssdat, containerExit)
				logger.WithField("terminated", timeExit).Warn("Container exited unexpectedly, restarting")

				if err := l.updateServiceStateInZK(ssdat, req); err != nil {
					logger.WithError(err).Error("Could not set state for stopped container")
					// TODO: we currently don't support containers restarting if
					// shut down during an outage, so don't bother
					return false
				}
				return true
			}
			if !handleContainerExit() {
				return
			}
		case <-cancel:
			logger.Debug("Host state listener received signal to cancel listening")
			return
		case <-l.shutdown:
			logger.Debug("Host state listener received signal to shutdown")
			// Container shutdown will be handled by the HostStateListener for all containers during shutdown
			return
		}

		close(done)
		done = make(chan struct{})
	}
}

// Gets a list of state IDs for all existing threads
//  Call l.mu.RLock() first
func (l *HostStateListener) getExistingStateIDs() []string {
	stateIds := make([]string, len(l.threads))
	i := 0
	for s := range l.threads {
		stateIds[i] = s
		i++
	}
	return stateIds
}

// Gets the ServiceState for an existing thread
//  Call l.mu.RLock() first
func (l *HostStateListener) getExistingState(stateID string) (*ServiceState, <-chan time.Time) {
	if thread, ok := l.threads[stateID]; ok {
		return thread.data, thread.exited
	}
	return nil, nil
}

// Adds a state to the internal thread list.
//  Call l.mu.Lock() first
func (l *HostStateListener) setExistingState(stateID string, data *ServiceState, containerExit <-chan time.Time) {
	l.threads[stateID] = struct {
		data   *ServiceState
		exited <-chan time.Time
	}{data, containerExit}
}

// Removes a state from the internal thread list
//  Call l.mu.Lock() first
func (l *HostStateListener) removeExistingState(stateID string) {
	delete(l.threads, stateID)
}

func (l *HostStateListener) updateServiceStateInZK(data *ServiceState, req StateRequest) error {
	return UpdateState(l.conn, req, func(s *State) bool {
		s.ServiceState = *data
		return true
	})
}

// Stops the running container, cleans up zk nodes, and removes the thread from the thread list
//  Call l.mu.Lock() first.
func (l *HostStateListener) cleanUpContainer(stateID string) {
	logger := plog.WithFields(log.Fields{
		"hostid":  l.hostID,
		"stateid": stateID,
	})

	// Parse the stateID
	hostID, serviceID, instanceID, err := ParseStateID(stateID)
	if err != nil || hostID != l.hostID {
		logger.WithField("hostidmatch", hostID == l.hostID).WithError(err).Warn("Could not clean up container: Invalid state id")
		return
	}

	logger = logger.WithFields(log.Fields{
		"serviceid":  serviceID,
		"instanceid": instanceID,
	})

	// Get the containerExit channel from our thread map
	_, containerExit := l.getExistingState(stateID)

	// stop the container
	if err := l.handler.StopContainer(serviceID, instanceID); err != nil {
		logger.WithError(err).Error("Could not stop container")
	} else if containerExit != nil {
		// wait for the container to exit
		time := <-containerExit
		logger.WithField("terminated", time).Debug("Container exited")
	}

	// Remove the container from our thread map
	l.removeExistingState(stateID)

	// delete the state from the coordinator
	req := StateRequest{
		PoolID:     "",
		HostID:     hostID,
		ServiceID:  serviceID,
		InstanceID: instanceID,
	}
	if err := DeleteState(l.conn, req); err != nil {
		logger.WithError(err).Warn("Could not delete state for stopped container")
		return
	}
}

func (l *HostStateListener) watchForShutdown() {
	<-l.shutdown
	l.mu.Lock()
	defer l.mu.Unlock()
	stateIDs := l.getExistingStateIDs()
	for _, s := range stateIDs {
		l.cleanUpContainer(s)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"neugram.io/eval"
	"neugram.io/eval/shell"
	"neugram.io/lang/tipe"
	"neugram.io/parser"

	"github.com/peterh/liner"
)

var (
	origMode liner.ModeApplier

	lineNg        *liner.State // ng-mode line reader
	historyNgFile = ""
	historyNg     = make(chan string, 1)
	historyShFile = ""
	historySh     = make(chan string, 1)

	prg *eval.Program
)

func exit(code int) {
	lineNg.Close()
	os.Exit(code)
}

func exitf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "ng: "+format, args...)
	exit(1)
}

func mode() liner.ModeApplier {
	m, err := liner.TerminalMode()
	if err != nil {
		exitf("terminal mode: %v", err)
	}
	return m
}

func main() {
	shell.Init()

	origMode = mode()
	lineNg = liner.NewLiner()
	loop()
}

func setWindowSize(env map[interface{}]interface{}) {
	// TODO windowsize
	// TODO
	// TODO
	// TODO
	/*
		rows, cols, err := job.WindowSize(os.Stderr.Fd())
		if err != nil {
			fmt.Printf("ng: could not get window size: %v\n", err)
		} else {
			// TODO: these are meant to be shell variables, not
			// environment variables. But then, how do programs
			// like `ls` read them?
			env["LINES"] = strconv.Itoa(rows)
			env["COLUMNS"] = strconv.Itoa(cols)
		}
	*/
}

func loop() {
	p := parser.New()
	prg = eval.New()

	// TODO this env setup could be done in neugram code
	env := prg.Cur.Lookup("env").Value.(map[interface{}]interface{})
	for _, s := range os.Environ() {
		i := strings.Index(s, "=")
		env[s[:i]] = s[i+1:]
	}
	setWindowSize(env)

	lineNg.SetCompleter(completer)

	if f, err := os.Open(historyShFile); err == nil {
		lineNg.SetMode("sh")
		lineNg.ReadHistory(f)
		f.Close()
	}
	go historyWriter(historyShFile, historySh)

	if f, err := os.Open(historyNgFile); err == nil {
		lineNg.SetMode("ng")
		lineNg.ReadHistory(f)
		f.Close()
	}
	go historyWriter(historyNgFile, historyNg)

	state := parser.StateStmt
	for {
		var (
			mode    string
			prompt  string
			history chan string
		)
		switch state {
		case parser.StateUnknown:
			mode, prompt, history = "ng", "??> ", historyNg
		case parser.StateStmt:
			mode, prompt, history = "ng", "ng> ", historyNg
		case parser.StateStmtPartial:
			mode, prompt, history = "ng", "..> ", historyNg
		case parser.StateCmd:
			mode, prompt, history = "sh", "$ ", historySh
		case parser.StateCmdPartial:
			mode, prompt, history = "sh", "..$ ", historySh
		default:
			exitf("unkown parser state: %v", state)
		}
		lineNg.SetMode(mode)
		data, err := lineNg.Prompt(prompt)
		if err != nil {
			if err == io.EOF {
				exit(0)
			}
			exitf("error reading input: %v", err)
		}
		if data == "" {
			continue
		}
		lineNg.AppendHistory(mode, data)
		history <- data
		res := p.ParseLine([]byte(data))

		for _, s := range res.Stmts {
			v, t, err := prg.Eval(s)
			if err != nil {
				fmt.Printf("eval error: %v\n", err)
				continue
			}
			switch len(v) {
			case 0:
			case 1:
				printValue(t, v[0])
				fmt.Print("\n")
			default:
				fmt.Println(v)
			}
		}
		for _, err := range res.Errs {
			fmt.Println(err.Error())
		}
		//editMode := mode()
		//origMode.ApplyMode()
		if len(res.Cmds) > 0 {
			shell.Env = prg.Environ()
		}
		for _, cmd := range res.Cmds {
			j := &shell.Job{
				Cmd:    cmd,
				Params: prg,
				Stdin:  os.Stdin,
				Stdout: os.Stdout,
				Stderr: os.Stderr,
			}
			if err := j.Start(); err != nil {
				fmt.Println(err)
				continue
			}
			done, err := j.Wait()
			if err != nil {
				fmt.Println(err)
				continue
			}
			if !done {
				break // TODO not right, instead we should just have one cmd, not Cmds here.
			}
		}
		//editMode.ApplyMode()
		state = res.State
	}
}

func printValue(t tipe.Type, v interface{}) {
	// This is, effectively, a primitive type-aware printf implementation
	// that understands the neugram evaluator data layout. A far better
	// version of this would be an "ngfmt" package, that implemented the
	// printing command in neugram, using a "ngreflect" package. But it
	// will be a while until I build a reflect package, so this will have
	// to do.
	//
	// Still: avoid putting too much machinary in this. At some point soon
	// it's not worth the effort.
	switch t := tipe.Underlying(t).(type) {
	case *tipe.Struct:
		fmt.Print("{")
		for i, name := range t.FieldNames {
			fmt.Printf("%s: ", name)
			printValue(t.Fields[i], v.(*eval.StructVal).Fields[i].Value)
			if i < len(t.FieldNames)-1 {
				fmt.Print(", ")
			}
		}
		fmt.Print("}")
	default:
		fmt.Print(v)
	}
}

func init() {
	if home := os.Getenv("HOME"); home != "" {
		historyNgFile = filepath.Join(home, ".ng_history")
		historyShFile = filepath.Join(home, ".ng_sh_history")
	}
}

func historyWriter(dst string, src <-chan string) {
	var batch []string
	ticker := time.Tick(250 * time.Millisecond)
	for {
		select {
		case line := <-src:
			batch = append(batch, line)
		case <-ticker:
			if len(batch) > 0 && dst != "" {
				// TODO: FcntlFlock
				f, err := os.OpenFile(dst, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0664)
				if err == nil {
					for _, line := range batch {
						fmt.Fprintf(f, "%s\n", line)
					}
					f.Close()
				}
			}
			batch = nil
		}
	}
}
<commit_msg>ng: support .ngshinit file<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"neugram.io/eval"
	"neugram.io/eval/shell"
	"neugram.io/lang/tipe"
	"neugram.io/parser"

	"github.com/peterh/liner"
)

var (
	origMode liner.ModeApplier

	lineNg        *liner.State // ng-mode line reader
	historyNgFile = ""
	historyNg     = make(chan string, 1)
	historyShFile = ""
	historySh     = make(chan string, 1)

	prg *eval.Program
)

func exit(code int) {
	lineNg.Close()
	os.Exit(code)
}

func exitf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "ng: "+format, args...)
	exit(1)
}

func mode() liner.ModeApplier {
	m, err := liner.TerminalMode()
	if err != nil {
		exitf("terminal mode: %v", err)
	}
	return m
}

func main() {
	shell.Init()

	origMode = mode()
	lineNg = liner.NewLiner()
	loop()
}

func setWindowSize(env map[interface{}]interface{}) {
	// TODO windowsize
	// TODO
	// TODO
	// TODO
	/*
		rows, cols, err := job.WindowSize(os.Stderr.Fd())
		if err != nil {
			fmt.Printf("ng: could not get window size: %v\n", err)
		} else {
			// TODO: these are meant to be shell variables, not
			// environment variables. But then, how do programs
			// like `ls` read them?
			env["LINES"] = strconv.Itoa(rows)
			env["COLUMNS"] = strconv.Itoa(cols)
		}
	*/
}

func loop() {
	p := parser.New()
	prg = eval.New()

	// TODO this env setup could be done in neugram code
	env := prg.Cur.Lookup("env").Value.(map[interface{}]interface{})
	for _, s := range os.Environ() {
		i := strings.Index(s, "=")
		env[s[:i]] = s[i+1:]
	}
	setWindowSize(env)

	lineNg.SetCompleter(completer)

	if f, err := os.Open(historyShFile); err == nil {
		lineNg.SetMode("sh")
		lineNg.ReadHistory(f)
		f.Close()
	}
	go historyWriter(historyShFile, historySh)

	if f, err := os.Open(historyNgFile); err == nil {
		lineNg.SetMode("ng")
		lineNg.ReadHistory(f)
		f.Close()
	}
	go historyWriter(historyNgFile, historyNg)

	state := parser.StateStmt
	if os.Args[0] == "ngsh" {
		initFile := filepath.Join(os.Getenv("HOME"), ".ngshinit")
		if f, err := os.Open(initFile); err == nil {
			scanner := bufio.NewScanner(f)
			for scanner.Scan() {
				res := p.ParseLine(scanner.Bytes())
				handleResult(res)
				state = res.State
			}
			if err := scanner.Err(); err != nil {
				exitf(".ngshinit: %v", err)
			}
			f.Close()
		}
		switch state {
		case parser.StateStmtPartial, parser.StateCmdPartial:
			exitf(".ngshinit: ends in a partial statement")
		case parser.StateStmt:
			res := p.ParseLine([]byte("$$"))
			handleResult(res)
			state = res.State
		}
	}

	for {
		var (
			mode    string
			prompt  string
			history chan string
		)
		switch state {
		case parser.StateUnknown:
			mode, prompt, history = "ng", "??> ", historyNg
		case parser.StateStmt:
			mode, prompt, history = "ng", "ng> ", historyNg
		case parser.StateStmtPartial:
			mode, prompt, history = "ng", "..> ", historyNg
		case parser.StateCmd:
			mode, prompt, history = "sh", "$ ", historySh
		case parser.StateCmdPartial:
			mode, prompt, history = "sh", "..$ ", historySh
		default:
			exitf("unkown parser state: %v", state)
		}
		lineNg.SetMode(mode)
		data, err := lineNg.Prompt(prompt)
		if err != nil {
			if err == io.EOF {
				exit(0)
			}
			exitf("error reading input: %v", err)
		}
		if data == "" {
			continue
		}
		lineNg.AppendHistory(mode, data)
		history <- data
		res := p.ParseLine([]byte(data))
		handleResult(res)
		state = res.State
	}
}

func handleResult(res parser.Result) {
	for _, s := range res.Stmts {
		v, t, err := prg.Eval(s)
		if err != nil {
			fmt.Printf("eval error: %v\n", err)
			continue
		}
		switch len(v) {
		case 0:
		case 1:
			printValue(t, v[0])
			fmt.Print("\n")
		default:
			fmt.Println(v)
		}
	}
	for _, err := range res.Errs {
		fmt.Println(err.Error())
	}
	//editMode := mode()
	//origMode.ApplyMode()
	if len(res.Cmds) > 0 {
		shell.Env = prg.Environ()
	}
	for _, cmd := range res.Cmds {
		j := &shell.Job{
			Cmd:    cmd,
			Params: prg,
			Stdin:  os.Stdin,
			Stdout: os.Stdout,
			Stderr: os.Stderr,
		}
		if err := j.Start(); err != nil {
			fmt.Println(err)
			continue
		}
		done, err := j.Wait()
		if err != nil {
			fmt.Println(err)
			continue
		}
		if !done {
			break // TODO not right, instead we should just have one cmd, not Cmds here.
		}
	}
	//editMode.ApplyMode()
}

func printValue(t tipe.Type, v interface{}) {
	// This is, effectively, a primitive type-aware printf implementation
	// that understands the neugram evaluator data layout. A far better
	// version of this would be an "ngfmt" package, that implemented the
	// printing command in neugram, using a "ngreflect" package. But it
	// will be a while until I build a reflect package, so this will have
	// to do.
	//
	// Still: avoid putting too much machinary in this. At some point soon
	// it's not worth the effort.
	switch t := tipe.Underlying(t).(type) {
	case *tipe.Struct:
		fmt.Print("{")
		for i, name := range t.FieldNames {
			fmt.Printf("%s: ", name)
			printValue(t.Fields[i], v.(*eval.StructVal).Fields[i].Value)
			if i < len(t.FieldNames)-1 {
				fmt.Print(", ")
			}
		}
		fmt.Print("}")
	default:
		fmt.Print(v)
	}
}

func init() {
	if home := os.Getenv("HOME"); home != "" {
		historyNgFile = filepath.Join(home, ".ng_history")
		historyShFile = filepath.Join(home, ".ngsh_history")
	}
}

func historyWriter(dst string, src <-chan string) {
	var batch []string
	ticker := time.Tick(250 * time.Millisecond)
	for {
		select {
		case line := <-src:
			batch = append(batch, line)
		case <-ticker:
			if len(batch) > 0 && dst != "" {
				// TODO: FcntlFlock
				f, err := os.OpenFile(dst, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0664)
				if err == nil {
					for _, line := range batch {
						fmt.Fprintf(f, "%s\n", line)
					}
					f.Close()
				}
			}
			batch = nil
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2013 The Camlistore 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.
*/

// This program runs camput in "dev" mode,
// to facilitate hacking on camlistore.
package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"camlistore.org/pkg/cmdmain"
	"camlistore.org/pkg/osutil"
)

type putCmd struct {
	// start of flag vars
	altkey bool
	path   string
	port   string
	tls    bool
	// end of flag vars

	verbose      bool   // set by CAMLI_QUIET
	camliSrcRoot string // the camlistore source tree
}

func init() {
	cmdmain.RegisterCommand("put", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := new(putCmd)
		flags.BoolVar(&cmd.altkey, "altkey", false, "Use different gpg key and password from the server's.")
		flags.BoolVar(&cmd.tls, "tls", false, "Use TLS.")
		flags.StringVar(&cmd.path, "path", "/", "Optional URL prefix path.")
		flags.StringVar(&cmd.port, "port", "3179", "Port camlistore is listening on.")
		return cmd
	})
}

func (c *putCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: devcam put [put_opts] camput_args\n")
}

func (c *putCmd) Examples() []string {
	return []string{
		"file --filenodes /mnt/camera/DCIM",
	}
}

func (c *putCmd) Describe() string {
	return "run camput in dev mode."
}

func (c *putCmd) RunCommand(args []string) error {
	err := c.checkFlags(args)
	if err != nil {
		return cmdmain.UsageError(fmt.Sprint(err))
	}
	c.camliSrcRoot, err = osutil.GoPackagePath("camlistore.org")
	if err != nil {
		return errors.New("Package camlistore.org not found in $GOPATH (or $GOPATH not defined).")
	}
	err = os.Chdir(c.camliSrcRoot)
	if err != nil {
		return fmt.Errorf("Could not chdir to %v: %v", c.camliSrcRoot, err)
	}
	if err := c.setEnvVars(); err != nil {
		return fmt.Errorf("Could not setup the env vars: %v", err)
	}

	blobserver := "http://localhost:" + c.port + c.path
	if c.tls {
		blobserver = strings.Replace(blobserver, "http://", "https://", 1)
	}

	cmdBin := filepath.Join(c.camliSrcRoot, "bin", "camput")
	cmdArgs := []string{
		"-verbose=" + strconv.FormatBool(c.verbose),
		"-server=" + blobserver,
	}
	cmdArgs = append(cmdArgs, args...)
	return runExec(cmdBin, cmdArgs)
}

func (c *putCmd) checkFlags(args []string) error {
	if _, err := strconv.ParseInt(c.port, 0, 0); err != nil {
		return fmt.Errorf("Invalid -port value: %q", c.port)
	}
	return nil
}

func (c *putCmd) build(name string) error {
	cmdName := "camput"
	target := filepath.Join("camlistore.org", "cmd", cmdName)
	binPath := filepath.Join(c.camliSrcRoot, "bin", cmdName)
	var modtime int64
	fi, err := os.Stat(binPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("Could not stat %v: %v", binPath, err)
		}
	} else {
		modtime = fi.ModTime().Unix()
	}
	args := []string{
		"run", "make.go",
		"--quiet",
		"--embed_static=false",
		fmt.Sprintf("--if_mods_since=%d", modtime),
		"--targets=" + target,
	}
	cmd := exec.Command("go", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("Error building %v: %v", target, err)
	}
	return nil
}

func (c *putCmd) setEnvVars() error {
	setenv("CAMLI_CONFIG_DIR", filepath.Join(c.camliSrcRoot, "config", "dev-client-dir"))
	setenv("CAMLI_SECRET_RING", filepath.Join(c.camliSrcRoot,
		filepath.FromSlash("pkg/jsonsign/testdata/test-secring.gpg")))
	setenv("CAMLI_KEYID", "26F5ABDA")
	setenv("CAMLI_AUTH", "userpass:camlistore:pass3179")
	setenv("CAMLI_DEV_KEYBLOBS", filepath.Join(c.camliSrcRoot,
		filepath.FromSlash("config/dev-client-dir/keyblobs")))
	if c.altkey {
		setenv("CAMLI_SECRET_RING", filepath.Join(c.camliSrcRoot,
			filepath.FromSlash("pkg/jsonsign/testdata/password-foo-secring.gpg")))
		setenv("CAMLI_KEYID", "C7C3E176")
		println("**\n** Note: password is \"foo\"\n**\n")
	}
	c.verbose, _ = strconv.ParseBool(os.Getenv("CAMLI_QUIET"))
	return nil
}
<commit_msg>devcam: add -nobuild flag to put.<commit_after>/*
Copyright 2013 The Camlistore 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.
*/

// This program runs camput in "dev" mode,
// to facilitate hacking on camlistore.
package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"camlistore.org/pkg/cmdmain"
	"camlistore.org/pkg/osutil"
)

type putCmd struct {
	// start of flag vars
	altkey  bool
	path    string
	port    string
	tls     bool
	noBuild bool
	// end of flag vars

	verbose      bool   // set by CAMLI_QUIET
	camliSrcRoot string // the camlistore source tree
}

func init() {
	cmdmain.RegisterCommand("put", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := new(putCmd)
		flags.BoolVar(&cmd.altkey, "altkey", false, "Use different gpg key and password from the server's.")
		flags.BoolVar(&cmd.tls, "tls", false, "Use TLS.")
		flags.StringVar(&cmd.path, "path", "/", "Optional URL prefix path.")
		flags.StringVar(&cmd.port, "port", "3179", "Port camlistore is listening on.")
		flags.BoolVar(&cmd.noBuild, "nobuild", false, "Do not rebuild anything.")
		return cmd
	})
}

func (c *putCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: devcam put [put_opts] camput_args\n")
}

func (c *putCmd) Examples() []string {
	return []string{
		"file --filenodes /mnt/camera/DCIM",
	}
}

func (c *putCmd) Describe() string {
	return "run camput in dev mode."
}

func (c *putCmd) RunCommand(args []string) error {
	err := c.checkFlags(args)
	if err != nil {
		return cmdmain.UsageError(fmt.Sprint(err))
	}
	c.camliSrcRoot, err = osutil.GoPackagePath("camlistore.org")
	if err != nil {
		return errors.New("Package camlistore.org not found in $GOPATH (or $GOPATH not defined).")
	}
	err = os.Chdir(c.camliSrcRoot)
	if err != nil {
		return fmt.Errorf("Could not chdir to %v: %v", c.camliSrcRoot, err)
	}
	if err := c.setEnvVars(); err != nil {
		return fmt.Errorf("Could not setup the env vars: %v", err)
	}

	blobserver := "http://localhost:" + c.port + c.path
	if c.tls {
		blobserver = strings.Replace(blobserver, "http://", "https://", 1)
	}

	cmdBin := filepath.Join(c.camliSrcRoot, "bin", "camput")
	cmdArgs := []string{
		"-verbose=" + strconv.FormatBool(c.verbose),
		"-server=" + blobserver,
	}
	cmdArgs = append(cmdArgs, args...)
	return runExec(cmdBin, cmdArgs)
}

func (c *putCmd) checkFlags(args []string) error {
	if _, err := strconv.ParseInt(c.port, 0, 0); err != nil {
		return fmt.Errorf("Invalid -port value: %q", c.port)
	}
	return nil
}

func (c *putCmd) build(name string) error {
	cmdName := "camput"
	target := filepath.Join("camlistore.org", "cmd", cmdName)
	binPath := filepath.Join(c.camliSrcRoot, "bin", cmdName)
	var modtime int64
	fi, err := os.Stat(binPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("Could not stat %v: %v", binPath, err)
		}
	} else {
		modtime = fi.ModTime().Unix()
	}
	args := []string{
		"run", "make.go",
		"--quiet",
		"--embed_static=false",
		fmt.Sprintf("--if_mods_since=%d", modtime),
		"--targets=" + target,
	}
	cmd := exec.Command("go", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("Error building %v: %v", target, err)
	}
	return nil
}

func (c *putCmd) setEnvVars() error {
	setenv("CAMLI_CONFIG_DIR", filepath.Join(c.camliSrcRoot, "config", "dev-client-dir"))
	setenv("CAMLI_SECRET_RING", filepath.Join(c.camliSrcRoot,
		filepath.FromSlash("pkg/jsonsign/testdata/test-secring.gpg")))
	setenv("CAMLI_KEYID", "26F5ABDA")
	setenv("CAMLI_AUTH", "userpass:camlistore:pass3179")
	setenv("CAMLI_DEV_KEYBLOBS", filepath.Join(c.camliSrcRoot,
		filepath.FromSlash("config/dev-client-dir/keyblobs")))
	if c.altkey {
		setenv("CAMLI_SECRET_RING", filepath.Join(c.camliSrcRoot,
			filepath.FromSlash("pkg/jsonsign/testdata/password-foo-secring.gpg")))
		setenv("CAMLI_KEYID", "C7C3E176")
		println("**\n** Note: password is \"foo\"\n**\n")
	}
	c.verbose, _ = strconv.ParseBool(os.Getenv("CAMLI_QUIET"))
	return nil
}
<|endoftext|>


<commit_before>package dht

import (
	"context"
	"io"

	inet "github.com/libp2p/go-libp2p-net"
	ma "github.com/multiformats/go-multiaddr"
	mstream "github.com/multiformats/go-multistream"
)

// netNotifiee defines methods to be used with the IpfsDHT
type netNotifiee IpfsDHT

func (nn *netNotifiee) DHT() *IpfsDHT {
	return (*IpfsDHT)(nn)
}

type peerTracker struct {
	refcount int
	cancel   func()
}

func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) {
	dht := nn.DHT()
	select {
	case <-dht.Process().Closing():
		return
	default:
	}

	dht.plk.Lock()
	defer dht.plk.Unlock()

	conn, ok := nn.peers[v.RemotePeer()]
	if ok {
		conn.refcount += 1
		return
	}

	ctx, cancel := context.WithCancel(dht.Context())

	nn.peers[v.RemotePeer()] = &peerTracker{
		refcount: 1,
		cancel:   cancel,
	}

	go func() {

		// Note: We *could* just check the peerstore to see if the remote side supports the dht
		// protocol, but its not clear that that information will make it into the peerstore
		// by the time this notification is sent. So just to be very careful, we brute force this
		// and open a new stream

		for {
			s, err := dht.host.NewStream(ctx, v.RemotePeer(), ProtocolDHT, ProtocolDHTOld)

			switch err {
			case nil:
				s.Close()
				dht.plk.Lock()
				defer dht.plk.Unlock()

				// Check if canceled under the lock.
				if ctx.Err() == nil {
					dht.Update(dht.Context(), v.RemotePeer())
				}
			case io.EOF:
				if ctx.Err() == nil {
					// Connection died but we may still have *an* open connection (context not canceled) so try again.
					continue
				}
			case mstream.ErrNotSupported:
				// Client mode only, don't bother adding them to our routing table
			default:
				// real error? thats odd
				log.Errorf("checking dht client type: %s", err)
			}
			return
		}
	}()
}

func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) {
	dht := nn.DHT()
	select {
	case <-dht.Process().Closing():
		return
	default:
	}

	dht.plk.Lock()
	defer dht.plk.Unlock()

	conn, ok := nn.peers[v.RemotePeer()]
	if !ok {
		// Unmatched disconnects are fine. It just means that we were
		// already connected when we registered the listener.
		return
	}
	conn.refcount -= 1
	if conn.refcount == 0 {
		delete(nn.peers, v.RemotePeer())
		conn.cancel()
		dht.routingTable.Remove(v.RemotePeer())
	}
}

func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {}
func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {}
func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr)      {}
func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {}
<commit_msg>notify: make locking clearer<commit_after>package dht

import (
	"context"
	"io"

	inet "github.com/libp2p/go-libp2p-net"
	ma "github.com/multiformats/go-multiaddr"
	mstream "github.com/multiformats/go-multistream"
)

// netNotifiee defines methods to be used with the IpfsDHT
type netNotifiee IpfsDHT

func (nn *netNotifiee) DHT() *IpfsDHT {
	return (*IpfsDHT)(nn)
}

type peerTracker struct {
	refcount int
	cancel   func()
}

func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) {
	dht := nn.DHT()
	select {
	case <-dht.Process().Closing():
		return
	default:
	}

	dht.plk.Lock()
	defer dht.plk.Unlock()

	conn, ok := nn.peers[v.RemotePeer()]
	if ok {
		conn.refcount += 1
		return
	}

	ctx, cancel := context.WithCancel(dht.Context())

	nn.peers[v.RemotePeer()] = &peerTracker{
		refcount: 1,
		cancel:   cancel,
	}

	// Note: We *could* just check the peerstore to see if the remote side supports the dht
	// protocol, but its not clear that that information will make it into the peerstore
	// by the time this notification is sent. So just to be very careful, we brute force this
	// and open a new stream
	go nn.testConnection(ctx, v)

}

func (nn *netNotifiee) testConnection(ctx context.Context, v inet.Conn) {
	dht := nn.DHT()
	for {
		s, err := dht.host.NewStream(ctx, v.RemotePeer(), ProtocolDHT, ProtocolDHTOld)

		switch err {
		case nil:
			s.Close()
			dht.plk.Lock()

			// Check if canceled under the lock.
			if ctx.Err() == nil {
				dht.Update(dht.Context(), v.RemotePeer())
			}

			dht.plk.Unlock()
		case io.EOF:
			if ctx.Err() == nil {
				// Connection died but we may still have *an* open connection (context not canceled) so try again.
				continue
			}
		case mstream.ErrNotSupported:
			// Client mode only, don't bother adding them to our routing table
		default:
			// real error? thats odd
			log.Errorf("checking dht client type: %s", err)
		}
		return
	}
}

func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) {
	dht := nn.DHT()
	select {
	case <-dht.Process().Closing():
		return
	default:
	}

	dht.plk.Lock()
	defer dht.plk.Unlock()

	conn, ok := nn.peers[v.RemotePeer()]
	if !ok {
		// Unmatched disconnects are fine. It just means that we were
		// already connected when we registered the listener.
		return
	}
	conn.refcount -= 1
	if conn.refcount == 0 {
		delete(nn.peers, v.RemotePeer())
		conn.cancel()
		dht.routingTable.Remove(v.RemotePeer())
	}
}

func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {}
func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {}
func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr)      {}
func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {}
<|endoftext|>


<commit_before>package goauth

import (
	"html/template"
	"net/http"
)

const (
	AuthorizeEnpoint = "/authorize"
	TokenEndpoint    = "/token"
)

func ApplyRoutes(mux *http.ServeMux) {
	mux.HandleFunc(AuthorizeEnpoint, authorizeHandler)
	mux.HandleFunc(TokenEndpoint, tokenHandler)
}

func RegisterAuthorizationCodeGrant(acg AuthorizationCodeGrant, tmpl *template.Template, ss *SessionStore) {
	tokenHandlers.AddHandler(GrantTypeAuthorizationCode, generateAuthCodeTokenRequestHandler(acg, ss))
	authorizeHandlers.AddHandler(ResponseTypeCode, generateAuthorizationCodeGrantHandler(acg, tmpl, ss))
}

func RegisterImplicitGrant(ig ImplicitGrant, ss *SessionStore) {
	authorizeHandlers.AddHandler(ResponseTypeToken, generateImplicitGrantHandler(ig, ss))
}

func RegisterResourceOwnerPasswordCredentialsGrant(ropcg ResourceOwnerPasswordCredentialsGrant, ss *SessionStore) {
	tokenHandlers.AddHandler(GrantTypePassword, generateResourceOwnerPasswordCredentialsGrant(ropcg, ss))
}

func RegisterClientCredentialsGrant(ccg ClientCredentialsGrant, ss *SessionStore) {
	tokenHandlers.AddHandler(GrantTypeClientCredentials, generateClientCredentialsGrantHandler(ccg, ss))
}
<commit_msg>refactors exported methods again, settling on http.Handler interface<commit_after>package goauth

import (
	"html/template"
	"net/http"
)

const (
	AuthorizeEnpoint = "/authorize"
	TokenEndpoint    = "/token"
)

type handler struct {
	mux *http.ServeMux
	ss  *SessionStore
}

func New() handler {
	mux := http.NewServeMux()
	mux.HandleFunc(AuthorizeEnpoint, authorizeHandler)
	mux.HandleFunc(TokenEndpoint, tokenHandler)
	return handler{
		mux,
		DefaultSessionStore,
	}
}

// UseSessionStore overrides the referenced SessionStore implementation for the handler.
func (h handler) UseSessionStore(ss *SessionStore) {
	*h.ss = *ss
}

// ServeHTTP implements the http.Handler interface.
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.mux.ServeHTTP(w, r)
}

// RegisterAuthorizationGrant registers handlers for the given AuthorizationCodeGrant interface using the provided template
// for performing user agent authentication.
func (h handler) RegisterAuthorizationCodeGrant(acg AuthorizationCodeGrant, tmpl *template.Template) {
	tokenHandlers.AddHandler(GrantTypeAuthorizationCode, generateAuthCodeTokenRequestHandler(acg, h.ss))
	authorizeHandlers.AddHandler(ResponseTypeCode, generateAuthorizationCodeGrantHandler(acg, tmpl, h.ss))
}

// RegisterImplicitGrant registers handlers for the provided ImplicitGrant interface.
func (h handler) RegisterImplicitGrant(ig ImplicitGrant) {
	authorizeHandlers.AddHandler(ResponseTypeToken, generateImplicitGrantHandler(ig, h.ss))
}

// RegisterResourceOwnerPasswordCredentialsGrant registers handlers for the provided ResourceOwnerPasswordCredentialsGrant interface.
func (h handler) RegisterResourceOwnerPasswordCredentialsGrant(ropcg ResourceOwnerPasswordCredentialsGrant) {
	tokenHandlers.AddHandler(GrantTypePassword, generateResourceOwnerPasswordCredentialsGrant(ropcg, h.ss))
}

// RegisterClientCredentialsGrant registers handlers for the provided ClientCredentialsGrant interface.
func (h handler) RegisterClientCredentialsGrant(ccg ClientCredentialsGrant) {
	tokenHandlers.AddHandler(GrantTypeClientCredentials, generateClientCredentialsGrantHandler(ccg, h.ss))
}
<|endoftext|>


<commit_before>/*
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 main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

const missingClientSecretsMessage = `
Please configure OAuth 2.0

To make this sample run, you need to populate the client_secrets.json file
found at:

   %v

with information from the {{ Google Cloud Console }}
{{ https://cloud.google.com/console }}

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
`

var (
	clientSecretsFile = flag.String("secrets", "client_secrets.json", "Client Secrets configuration")
	cache             = flag.String("cache", "request.token", "Token cache file")
)

// CallbackStatus is returned from the oauth2 callback
type CallbackStatus struct {
	code  string
	state string
	err   error
}

// Cache specifies the methods that implement a Token cache.
type Cache interface {
	Token() (*oauth2.Token, error)
	PutToken(*oauth2.Token) error
}

// CacheFile implements Cache. Its value is the name of the file in which
// the Token is stored in JSON format.
type CacheFile string

// ClientConfig is a data structure definition for the client_secrets.json file.
// The code unmarshals the JSON configuration file into this structure.
type ClientConfig struct {
	ClientID     string   `json:"client_id"`
	ClientSecret string   `json:"client_secret"`
	RedirectURIs []string `json:"redirect_uris"`
	AuthURI      string   `json:"auth_uri"`
	TokenURI     string   `json:"token_uri"`
}

// Config is a root-level configuration object.
type Config struct {
	Installed ClientConfig `json:"installed"`
	Web       ClientConfig `json:"web"`
}

// openURL opens a browser window to the specified location.
// This code originally appeared at:
//   http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go
func openURL(url string) error {
	var err error
	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", url).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://localhost:4001/").Start()
	case "darwin":
		err = exec.Command("open", url).Start()
	default:
		err = fmt.Errorf("Cannot open URL %s on this platform", url)
	}
	return err
}

// readConfig reads the configuration from clientSecretsFile.
// It returns an oauth configuration object for use with the Google API client.
func readConfig(scope string) (*oauth2.Config, error) {
	// Read the secrets file
	data, err := ioutil.ReadFile(*clientSecretsFile)
	if err != nil {
		pwd, _ := os.Getwd()
		fullPath := filepath.Join(pwd, *clientSecretsFile)
		return nil, fmt.Errorf(missingClientSecretsMessage, fullPath)
	}

	cfg := new(Config)
	err = json.Unmarshal(data, &cfg)
	if err != nil {
		return nil, err
	}

	var oCfg *oauth2.Config
	if len(cfg.Web.RedirectURIs) > 0 {
		oCfg = &oauth2.Config{
			ClientID:     cfg.Web.ClientID,
			ClientSecret: cfg.Web.ClientSecret,
			Scopes:       []string{scope},
			Endpoint: oauth2.Endpoint{
				AuthURL:  cfg.Web.AuthURI,
				TokenURL: cfg.Web.TokenURI,
			},
			RedirectURL: cfg.Web.RedirectURIs[0],
		}
	} else if len(cfg.Installed.RedirectURIs) > 0 {
		oCfg = &oauth2.Config{
			ClientID:     cfg.Installed.ClientID,
			ClientSecret: cfg.Installed.ClientSecret,
			Scopes:       []string{scope},
			Endpoint: oauth2.Endpoint{
				AuthURL:  cfg.Installed.AuthURI,
				TokenURL: cfg.Installed.TokenURI,
			},
			RedirectURL: cfg.Installed.RedirectURIs[0],
		}
	} else {
		return nil, errors.New("Must specify a redirect URI in config file or when creating OAuth client")
	}

	return oCfg, nil
}

// startWebServer starts a web server that listens on http://localhost:8080.
// The webserver waits for an oauth code in the three-legged auth flow.
func startWebServer() (callbackCh chan CallbackStatus, err error) {
	listener, err := net.Listen("tcp", "localhost:8080")
	if err != nil {
		return nil, err
	}
	callbackCh = make(chan CallbackStatus)
	go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		cbs := CallbackStatus{}
		cbs.state = r.FormValue("state")
		cbs.code = r.FormValue("code")
		callbackCh <- cbs // send code to OAuth flow
		listener.Close()
		w.Header().Set("Content-Type", "text/plain")
		fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", cbs.code)
	}))

	return callbackCh, nil
}

// buildOAuthHTTPClient takes the user through the three-legged OAuth flow.
// It opens a browser in the native OS or outputs a URL, then blocks until
// the redirect completes to the /oauth2callback URI.
// It returns an instance of an HTTP client that can be passed to the
// constructor of the YouTube client.
func buildOAuthHTTPClient(ctx context.Context, scope string) (*http.Client, error) {
	config, err := readConfig(scope)
	if err != nil {
		msg := fmt.Sprintf("Cannot read configuration file: %v", err)
		return nil, errors.New(msg)
	}

	// Try to read the token from the cache file.
	// If an error occurs, do the three-legged OAuth flow because
	// the token is invalid or doesn't exist.
	tokenCache := CacheFile(*cache)
	token, err := tokenCache.Token()
	if err != nil {

		// You must always provide a non-zero string and validate that it matches
		// the state query parameter on your redirect callback
		randState := fmt.Sprintf("st%d", time.Now().UnixNano())

		// Start web server.
		// This is how this program receives the authorization code
		// when the browser redirects.
		callbackCh, err := startWebServer()
		if err != nil {
			return nil, err
		}

		url := config.AuthCodeURL(randState, oauth2.AccessTypeOffline, oauth2.ApprovalForce)
		err = openURL(url)
		if err != nil {
			fmt.Println("Visit the URL below to get a code.",
				" This program will pause until the site is visted.")
		} else {
			fmt.Println("Your browser has been opened to an authorization URL.",
				" This program will resume once authorization has been provided.")
		}
		fmt.Println(url)

		// Wait for the web server to get the code.
		cbs := <-callbackCh

		if cbs.state != randState {
			return nil, fmt.Errorf("expecting state '%s', received state '%s'", randState, cbs.state)
		}

		token, err = config.Exchange(oauth2.NoContext, cbs.code)
		if err != nil {
			return nil, err
		}
		err = tokenCache.PutToken(token)
		if err != nil {
			return nil, err
		}
	}

	return config.Client(ctx, token), nil
}

// Token retreives the token from the token cache
func (f CacheFile) Token() (*oauth2.Token, error) {
	file, err := os.Open(string(f))
	if err != nil {
		return nil, fmt.Errorf("CacheFile.Token: %s", err.Error())
	}
	defer file.Close()
	tok := &oauth2.Token{}
	if err := json.NewDecoder(file).Decode(tok); err != nil {
		return nil, fmt.Errorf("CacheFile.Token: %s", err.Error())
	}
	return tok, nil
}

// PutToken stores the token in the token cache
func (f CacheFile) PutToken(tok *oauth2.Token) error {
	file, err := os.OpenFile(string(f), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return fmt.Errorf("CacheFile.PutToken: %s", err.Error())
	}
	if err := json.NewEncoder(file).Encode(tok); err != nil {
		file.Close()
		return fmt.Errorf("CacheFile.PutToken: %s", err.Error())
	}
	if err := file.Close(); err != nil {
		return fmt.Errorf("CacheFile.PutToken: %s", err.Error())
	}
	return nil
}
<commit_msg>Fixed opening URL in browser for authorisation on windows<commit_after>/*
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 main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

const missingClientSecretsMessage = `
Please configure OAuth 2.0

To make this sample run, you need to populate the client_secrets.json file
found at:

   %v

with information from the {{ Google Cloud Console }}
{{ https://cloud.google.com/console }}

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
`

var (
	clientSecretsFile = flag.String("secrets", "client_secrets.json", "Client Secrets configuration")
	cache             = flag.String("cache", "request.token", "Token cache file")
)

// CallbackStatus is returned from the oauth2 callback
type CallbackStatus struct {
	code  string
	state string
	err   error
}

// Cache specifies the methods that implement a Token cache.
type Cache interface {
	Token() (*oauth2.Token, error)
	PutToken(*oauth2.Token) error
}

// CacheFile implements Cache. Its value is the name of the file in which
// the Token is stored in JSON format.
type CacheFile string

// ClientConfig is a data structure definition for the client_secrets.json file.
// The code unmarshals the JSON configuration file into this structure.
type ClientConfig struct {
	ClientID     string   `json:"client_id"`
	ClientSecret string   `json:"client_secret"`
	RedirectURIs []string `json:"redirect_uris"`
	AuthURI      string   `json:"auth_uri"`
	TokenURI     string   `json:"token_uri"`
}

// Config is a root-level configuration object.
type Config struct {
	Installed ClientConfig `json:"installed"`
	Web       ClientConfig `json:"web"`
}

// openURL opens a browser window to the specified location.
// This code originally appeared at:
//   http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go
func openURL(url string) error {
	var err error
	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", url).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
	case "darwin":
		err = exec.Command("open", url).Start()
	default:
		err = fmt.Errorf("Cannot open URL %s on this platform", url)
	}
	return err
}

// readConfig reads the configuration from clientSecretsFile.
// It returns an oauth configuration object for use with the Google API client.
func readConfig(scope string) (*oauth2.Config, error) {
	// Read the secrets file
	data, err := ioutil.ReadFile(*clientSecretsFile)
	if err != nil {
		pwd, _ := os.Getwd()
		fullPath := filepath.Join(pwd, *clientSecretsFile)
		return nil, fmt.Errorf(missingClientSecretsMessage, fullPath)
	}

	cfg := new(Config)
	err = json.Unmarshal(data, &cfg)
	if err != nil {
		return nil, err
	}

	var oCfg *oauth2.Config
	if len(cfg.Web.RedirectURIs) > 0 {
		oCfg = &oauth2.Config{
			ClientID:     cfg.Web.ClientID,
			ClientSecret: cfg.Web.ClientSecret,
			Scopes:       []string{scope},
			Endpoint: oauth2.Endpoint{
				AuthURL:  cfg.Web.AuthURI,
				TokenURL: cfg.Web.TokenURI,
			},
			RedirectURL: cfg.Web.RedirectURIs[0],
		}
	} else if len(cfg.Installed.RedirectURIs) > 0 {
		oCfg = &oauth2.Config{
			ClientID:     cfg.Installed.ClientID,
			ClientSecret: cfg.Installed.ClientSecret,
			Scopes:       []string{scope},
			Endpoint: oauth2.Endpoint{
				AuthURL:  cfg.Installed.AuthURI,
				TokenURL: cfg.Installed.TokenURI,
			},
			RedirectURL: cfg.Installed.RedirectURIs[0],
		}
	} else {
		return nil, errors.New("Must specify a redirect URI in config file or when creating OAuth client")
	}

	return oCfg, nil
}

// startWebServer starts a web server that listens on http://localhost:8080.
// The webserver waits for an oauth code in the three-legged auth flow.
func startWebServer() (callbackCh chan CallbackStatus, err error) {
	listener, err := net.Listen("tcp", "localhost:8080")
	if err != nil {
		return nil, err
	}
	callbackCh = make(chan CallbackStatus)
	go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		cbs := CallbackStatus{}
		cbs.state = r.FormValue("state")
		cbs.code = r.FormValue("code")
		callbackCh <- cbs // send code to OAuth flow
		listener.Close()
		w.Header().Set("Content-Type", "text/plain")
		fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", cbs.code)
	}))

	return callbackCh, nil
}

// buildOAuthHTTPClient takes the user through the three-legged OAuth flow.
// It opens a browser in the native OS or outputs a URL, then blocks until
// the redirect completes to the /oauth2callback URI.
// It returns an instance of an HTTP client that can be passed to the
// constructor of the YouTube client.
func buildOAuthHTTPClient(ctx context.Context, scope string) (*http.Client, error) {
	config, err := readConfig(scope)
	if err != nil {
		msg := fmt.Sprintf("Cannot read configuration file: %v", err)
		return nil, errors.New(msg)
	}

	// Try to read the token from the cache file.
	// If an error occurs, do the three-legged OAuth flow because
	// the token is invalid or doesn't exist.
	tokenCache := CacheFile(*cache)
	token, err := tokenCache.Token()
	if err != nil {

		// You must always provide a non-zero string and validate that it matches
		// the state query parameter on your redirect callback
		randState := fmt.Sprintf("st%d", time.Now().UnixNano())

		// Start web server.
		// This is how this program receives the authorization code
		// when the browser redirects.
		callbackCh, err := startWebServer()
		if err != nil {
			return nil, err
		}

		url := config.AuthCodeURL(randState, oauth2.AccessTypeOffline, oauth2.ApprovalForce)
		err = openURL(url)
		if err != nil {
			fmt.Println("Visit the URL below to get a code.",
				" This program will pause until the site is visted.")
		} else {
			fmt.Println("Your browser has been opened to an authorization URL.",
				" This program will resume once authorization has been provided.")
		}
		fmt.Println(url)

		// Wait for the web server to get the code.
		cbs := <-callbackCh

		if cbs.state != randState {
			return nil, fmt.Errorf("expecting state '%s', received state '%s'", randState, cbs.state)
		}

		token, err = config.Exchange(oauth2.NoContext, cbs.code)
		if err != nil {
			return nil, err
		}
		err = tokenCache.PutToken(token)
		if err != nil {
			return nil, err
		}
	}

	return config.Client(ctx, token), nil
}

// Token retreives the token from the token cache
func (f CacheFile) Token() (*oauth2.Token, error) {
	file, err := os.Open(string(f))
	if err != nil {
		return nil, fmt.Errorf("CacheFile.Token: %s", err.Error())
	}
	defer file.Close()
	tok := &oauth2.Token{}
	if err := json.NewDecoder(file).Decode(tok); err != nil {
		return nil, fmt.Errorf("CacheFile.Token: %s", err.Error())
	}
	return tok, nil
}

// PutToken stores the token in the token cache
func (f CacheFile) PutToken(tok *oauth2.Token) error {
	file, err := os.OpenFile(string(f), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return fmt.Errorf("CacheFile.PutToken: %s", err.Error())
	}
	if err := json.NewEncoder(file).Encode(tok); err != nil {
		file.Close()
		return fmt.Errorf("CacheFile.PutToken: %s", err.Error())
	}
	if err := file.Close(); err != nil {
		return fmt.Errorf("CacheFile.PutToken: %s", err.Error())
	}
	return nil
}
<|endoftext|>


<commit_before>package goshopify

import (
	"fmt"
	"time"

	"github.com/shopspring/decimal"
)

const ordersBasePath = "admin/orders"

// OrderService is an interface for interfacing with the orders endpoints of
// the Shopify API.
// See: https://help.shopify.com/api/reference/order
type OrderService interface {
	List(interface{}) ([]Order, error)
	Count(interface{}) (int, error)
	Get(int, interface{}) (*Order, error)
}

// OrderServiceOp handles communication with the order related methods of the
// Shopify API.
type OrderServiceOp struct {
	client *Client
}

// A struct for all available order list options.
// See: https://help.shopify.com/api/reference/order#index
type OrderListOptions struct {
	Page              int       `url:"page,omitempty"`
	Limit             int       `url:"limit,omitempty"`
	SinceID           int       `url:"since_id,omitempty"`
	Status            string    `url:"status,omitempty"`
	FinancialStatus   string    `url:"financial_status,omitempty"`
	FulfillmentStatus string    `url:"fulfillment_status,omitempty"`
	CreatedAtMin      time.Time `url:"created_at_min,omitempty"`
	CreatedAtMax      time.Time `url:"created_at_max,omitempty"`
	UpdatedAtMin      time.Time `url:"updated_at_min,omitempty"`
	UpdatedAtMax      time.Time `url:"updated_at_max,omitempty"`
	ProcessedAtMin    time.Time `url:"processed_at_min,omitempty"`
	ProcessedAtMax    time.Time `url:"processed_at_max,omitempty"`
	Fields            string    `url:"fields,omitempty"`
}

// Order represents a Shopify order
type Order struct {
	ID                    int              `json:"id"`
	Name                  string           `json:"name"`
	Email                 string           `json:"email"`
	CreatedAt             *time.Time       `json:"created_at"`
	UpdatedAt             *time.Time       `json:"updated_at"`
	ClosedAt              *time.Time       `json:"closed_at"`
	ProcessedAt           *time.Time       `json:"processed_at"`
	BillingAddress        *Address         `json:"billing_address"`
	ShippingAddress       *Address         `json:"shipping_address"`
	Currency              string           `json:"currency"`
	TotalPrice            *decimal.Decimal `json:"total_price"`
	SubtotalPrice         *decimal.Decimal `json:"subtotal_price"`
	TotalDiscounts        *decimal.Decimal `json:"total_discounts"`
	TotalLineItemsPrice   *decimal.Decimal `json:"total_line_items_price"`
	TotalTax              *decimal.Decimal `json:"total_tax"`
	TotalWeight           int              `json:"total_weight"`
	FinancialStatus       string           `json:"financial_status"`
	FulfillmentStatus     string           `json:"fulfillment_status"`
	Token                 string           `json:"token"`
	CartToken             string           `json:"cart_token"`
	Number                int              `json:"number"`
	OrderNumber           int              `json:"order_number"`
	Note                  string           `json:"note"`
	Test                  bool             `json:"test"`
	BrowserIp             string           `json:"browser_ip"`
	BuyerAcceptsMarketing bool             `json:"buyer_accepts_marketing"`
	CancelReason          string           `json:"cancel_reason"`
	NoteAttributes        []NoteAttribute  `json:"note_attributes"`
	DiscountCodes         []DiscountCode   `json:"discount_codes"`
	LineItems             []LineItem       `json:"line_items"`
}

type Address struct {
	ID           int    `json:"id"`
	Address1     string `json:"address1"`
	Address2     string `json:"address2"`
	City         string `json:"city"`
	Company      string `json:"company"`
	Country      string `json:"country"`
	CountryCode  string `json:"country_code"`
	FirstName    string `json:"first_name"`
	LastName     string `json:"last_name"`
	Latitude     string `json:"latitude"`
	Longitude    string `json:"longitude"`
	Name         string `json:"name"`
	Phone        string `json:"phone"`
	Province     string `json:"province"`
	ProvinceCode string `json:"province_code"`
	Zip          string `json:"zip"`
}

type DiscountCode struct {
	Amount *decimal.Decimal `json:"amount"`
	Code   string           `json:"code"`
	Type   string           `json:"type"`
}

type LineItem struct {
	ID            int              `json:"id"`
	ProductID     int              `json:"product_id"`
	VariantID     int              `json:"variant_id"`
	Quantity      int              `json:"quantity"`
	Price         *decimal.Decimal `json:"price"`
	TotalDiscount *decimal.Decimal `json:"total_discount"`
	Title         string           `json:"title"`
	VariantTitle  string           `json:"variant_title"`
	Name          string           `json:"name"`
	SKU           string           `json:"sku"`
	Vendor        string           `json:"vendor"`
	GiftCard      bool             `json:"gift_card"`
	Taxable       bool             `json:"taxable"`
}

type LineItemProperty struct {
	Message string `json:"message"`
}

type NoteAttribute struct {
	Name  string `json:"Name"`
	Value string `json:"Value"`
}

// Represents the result from the orders/X.json endpoint
type OrderResource struct {
	Order *Order `json:"order"`
}

// Represents the result from the orders.json endpoint
type OrdersResource struct {
	Orders []Order `json:"orders"`
}

// List orders
func (s *OrderServiceOp) List(options interface{}) ([]Order, error) {
	path := fmt.Sprintf("%s.json", ordersBasePath)
	resource := new(OrdersResource)
	err := s.client.Get(path, resource, options)
	return resource.Orders, err
}

// Count orders
func (s *OrderServiceOp) Count(options interface{}) (int, error) {
	path := fmt.Sprintf("%s/count.json", ordersBasePath)
	return s.client.Count(path, options)
}

// Get individual order
func (s *OrderServiceOp) Get(orderID int, options interface{}) (*Order, error) {
	path := fmt.Sprintf("%s/%d.json", ordersBasePath, orderID)
	resource := new(OrderResource)
	err := s.client.Get(path, resource, options)
	return resource.Order, err
}
<commit_msg>Fix wrong latitude/longitude types (#13)<commit_after>package goshopify

import (
	"fmt"
	"time"

	"github.com/shopspring/decimal"
)

const ordersBasePath = "admin/orders"

// OrderService is an interface for interfacing with the orders endpoints of
// the Shopify API.
// See: https://help.shopify.com/api/reference/order
type OrderService interface {
	List(interface{}) ([]Order, error)
	Count(interface{}) (int, error)
	Get(int, interface{}) (*Order, error)
}

// OrderServiceOp handles communication with the order related methods of the
// Shopify API.
type OrderServiceOp struct {
	client *Client
}

// A struct for all available order list options.
// See: https://help.shopify.com/api/reference/order#index
type OrderListOptions struct {
	Page              int       `url:"page,omitempty"`
	Limit             int       `url:"limit,omitempty"`
	SinceID           int       `url:"since_id,omitempty"`
	Status            string    `url:"status,omitempty"`
	FinancialStatus   string    `url:"financial_status,omitempty"`
	FulfillmentStatus string    `url:"fulfillment_status,omitempty"`
	CreatedAtMin      time.Time `url:"created_at_min,omitempty"`
	CreatedAtMax      time.Time `url:"created_at_max,omitempty"`
	UpdatedAtMin      time.Time `url:"updated_at_min,omitempty"`
	UpdatedAtMax      time.Time `url:"updated_at_max,omitempty"`
	ProcessedAtMin    time.Time `url:"processed_at_min,omitempty"`
	ProcessedAtMax    time.Time `url:"processed_at_max,omitempty"`
	Fields            string    `url:"fields,omitempty"`
}

// Order represents a Shopify order
type Order struct {
	ID                    int              `json:"id"`
	Name                  string           `json:"name"`
	Email                 string           `json:"email"`
	CreatedAt             *time.Time       `json:"created_at"`
	UpdatedAt             *time.Time       `json:"updated_at"`
	ClosedAt              *time.Time       `json:"closed_at"`
	ProcessedAt           *time.Time       `json:"processed_at"`
	BillingAddress        *Address         `json:"billing_address"`
	ShippingAddress       *Address         `json:"shipping_address"`
	Currency              string           `json:"currency"`
	TotalPrice            *decimal.Decimal `json:"total_price"`
	SubtotalPrice         *decimal.Decimal `json:"subtotal_price"`
	TotalDiscounts        *decimal.Decimal `json:"total_discounts"`
	TotalLineItemsPrice   *decimal.Decimal `json:"total_line_items_price"`
	TotalTax              *decimal.Decimal `json:"total_tax"`
	TotalWeight           int              `json:"total_weight"`
	FinancialStatus       string           `json:"financial_status"`
	FulfillmentStatus     string           `json:"fulfillment_status"`
	Token                 string           `json:"token"`
	CartToken             string           `json:"cart_token"`
	Number                int              `json:"number"`
	OrderNumber           int              `json:"order_number"`
	Note                  string           `json:"note"`
	Test                  bool             `json:"test"`
	BrowserIp             string           `json:"browser_ip"`
	BuyerAcceptsMarketing bool             `json:"buyer_accepts_marketing"`
	CancelReason          string           `json:"cancel_reason"`
	NoteAttributes        []NoteAttribute  `json:"note_attributes"`
	DiscountCodes         []DiscountCode   `json:"discount_codes"`
	LineItems             []LineItem       `json:"line_items"`
}

type Address struct {
	ID           int     `json:"id"`
	Address1     string  `json:"address1"`
	Address2     string  `json:"address2"`
	City         string  `json:"city"`
	Company      string  `json:"company"`
	Country      string  `json:"country"`
	CountryCode  string  `json:"country_code"`
	FirstName    string  `json:"first_name"`
	LastName     string  `json:"last_name"`
	Latitude     float64 `json:"latitude"`
	Longitude    float64 `json:"longitude"`
	Name         string  `json:"name"`
	Phone        string  `json:"phone"`
	Province     string  `json:"province"`
	ProvinceCode string  `json:"province_code"`
	Zip          string  `json:"zip"`
}

type DiscountCode struct {
	Amount *decimal.Decimal `json:"amount"`
	Code   string           `json:"code"`
	Type   string           `json:"type"`
}

type LineItem struct {
	ID            int              `json:"id"`
	ProductID     int              `json:"product_id"`
	VariantID     int              `json:"variant_id"`
	Quantity      int              `json:"quantity"`
	Price         *decimal.Decimal `json:"price"`
	TotalDiscount *decimal.Decimal `json:"total_discount"`
	Title         string           `json:"title"`
	VariantTitle  string           `json:"variant_title"`
	Name          string           `json:"name"`
	SKU           string           `json:"sku"`
	Vendor        string           `json:"vendor"`
	GiftCard      bool             `json:"gift_card"`
	Taxable       bool             `json:"taxable"`
}

type LineItemProperty struct {
	Message string `json:"message"`
}

type NoteAttribute struct {
	Name  string `json:"Name"`
	Value string `json:"Value"`
}

// Represents the result from the orders/X.json endpoint
type OrderResource struct {
	Order *Order `json:"order"`
}

// Represents the result from the orders.json endpoint
type OrdersResource struct {
	Orders []Order `json:"orders"`
}

// List orders
func (s *OrderServiceOp) List(options interface{}) ([]Order, error) {
	path := fmt.Sprintf("%s.json", ordersBasePath)
	resource := new(OrdersResource)
	err := s.client.Get(path, resource, options)
	return resource.Orders, err
}

// Count orders
func (s *OrderServiceOp) Count(options interface{}) (int, error) {
	path := fmt.Sprintf("%s/count.json", ordersBasePath)
	return s.client.Count(path, options)
}

// Get individual order
func (s *OrderServiceOp) Get(orderID int, options interface{}) (*Order, error) {
	path := fmt.Sprintf("%s/%d.json", ordersBasePath, orderID)
	resource := new(OrderResource)
	err := s.client.Get(path, resource, options)
	return resource.Order, err
}
<|endoftext|>


<commit_before>package container // import "github.com/docker/docker/integration/container"

import (
	"context"
	"fmt"
	"testing"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/docker/docker/integration/internal/container"
	"gotest.tools/assert"
	is "gotest.tools/assert/cmp"
	"gotest.tools/skip"
)

func TestCopyFromContainerPathDoesNotExist(t *testing.T) {
	defer setupTest(t)()
	skip.If(t, testEnv.OSType == "windows")

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	_, _, err := apiclient.CopyFromContainer(ctx, cid, "/dne")
	assert.Check(t, client.IsErrNotFound(err))
	expected := fmt.Sprintf("No such container:path: %s:%s", cid, "/dne")
	assert.Check(t, is.ErrorContains(err, expected))
}

func TestCopyFromContainerPathIsNotDir(t *testing.T) {
	defer setupTest(t)()
	skip.If(t, testEnv.OSType == "windows")

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	_, _, err := apiclient.CopyFromContainer(ctx, cid, "/etc/passwd/")
	assert.Assert(t, is.ErrorContains(err, "not a directory"))
}

func TestCopyToContainerPathDoesNotExist(t *testing.T) {
	defer setupTest(t)()
	skip.If(t, testEnv.OSType == "windows")

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	err := apiclient.CopyToContainer(ctx, cid, "/dne", nil, types.CopyToContainerOptions{})
	assert.Check(t, client.IsErrNotFound(err))
	expected := fmt.Sprintf("No such container:path: %s:%s", cid, "/dne")
	assert.Check(t, is.ErrorContains(err, expected))
}

func TestCopyToContainerPathIsNotDir(t *testing.T) {
	defer setupTest(t)()
	skip.If(t, testEnv.OSType == "windows")

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	err := apiclient.CopyToContainer(ctx, cid, "/etc/passwd/", nil, types.CopyToContainerOptions{})
	assert.Assert(t, is.ErrorContains(err, "not a directory"))
}
<commit_msg>Add test for copying entire container rootfs<commit_after>package container // import "github.com/docker/docker/integration/container"

import (
	"archive/tar"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"testing"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/internal/test/fakecontext"
	"github.com/docker/docker/pkg/jsonmessage"
	"gotest.tools/assert"
	is "gotest.tools/assert/cmp"
	"gotest.tools/skip"
)

func TestCopyFromContainerPathDoesNotExist(t *testing.T) {
	defer setupTest(t)()
	skip.If(t, testEnv.OSType == "windows")

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	_, _, err := apiclient.CopyFromContainer(ctx, cid, "/dne")
	assert.Check(t, client.IsErrNotFound(err))
	expected := fmt.Sprintf("No such container:path: %s:%s", cid, "/dne")
	assert.Check(t, is.ErrorContains(err, expected))
}

func TestCopyFromContainerPathIsNotDir(t *testing.T) {
	defer setupTest(t)()
	skip.If(t, testEnv.OSType == "windows")

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	_, _, err := apiclient.CopyFromContainer(ctx, cid, "/etc/passwd/")
	assert.Assert(t, is.ErrorContains(err, "not a directory"))
}

func TestCopyToContainerPathDoesNotExist(t *testing.T) {
	defer setupTest(t)()
	skip.If(t, testEnv.OSType == "windows")

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	err := apiclient.CopyToContainer(ctx, cid, "/dne", nil, types.CopyToContainerOptions{})
	assert.Check(t, client.IsErrNotFound(err))
	expected := fmt.Sprintf("No such container:path: %s:%s", cid, "/dne")
	assert.Check(t, is.ErrorContains(err, expected))
}

func TestCopyToContainerPathIsNotDir(t *testing.T) {
	defer setupTest(t)()
	skip.If(t, testEnv.OSType == "windows")

	ctx := context.Background()
	apiclient := testEnv.APIClient()
	cid := container.Create(ctx, t, apiclient)

	err := apiclient.CopyToContainer(ctx, cid, "/etc/passwd/", nil, types.CopyToContainerOptions{})
	assert.Assert(t, is.ErrorContains(err, "not a directory"))
}

func TestCopyFromContainerRoot(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
	defer setupTest(t)()

	ctx := context.Background()
	apiClient := testEnv.APIClient()

	dir, err := ioutil.TempDir("", t.Name())
	assert.NilError(t, err)
	defer os.RemoveAll(dir)

	buildCtx := fakecontext.New(t, dir, fakecontext.WithFile("foo", "hello"), fakecontext.WithFile("baz", "world"), fakecontext.WithDockerfile(`
		FROM scratch
		COPY foo /foo
		COPY baz /bar/baz
		CMD /fake
	`))
	defer buildCtx.Close()

	resp, err := apiClient.ImageBuild(ctx, buildCtx.AsTarReader(t), types.ImageBuildOptions{})
	assert.NilError(t, err)
	defer resp.Body.Close()

	var imageID string
	err = jsonmessage.DisplayJSONMessagesStream(resp.Body, ioutil.Discard, 0, false, func(msg jsonmessage.JSONMessage) {
		var r types.BuildResult
		assert.NilError(t, json.Unmarshal(*msg.Aux, &r))
		imageID = r.ID
	})
	assert.NilError(t, err)
	assert.Assert(t, imageID != "")

	cid := container.Create(ctx, t, apiClient, container.WithImage(imageID))

	rdr, _, err := apiClient.CopyFromContainer(ctx, cid, "/")
	assert.NilError(t, err)
	defer rdr.Close()

	tr := tar.NewReader(rdr)
	expect := map[string]string{
		"/foo":     "hello",
		"/bar/baz": "world",
	}
	found := make(map[string]bool, 2)
	var numFound int
	for {
		h, err := tr.Next()
		if err == io.EOF {
			break
		}
		assert.NilError(t, err)

		expected, exists := expect[h.Name]
		if !exists {
			// this archive will have extra stuff in it since we are copying from root
			// and docker adds a bunch of stuff
			continue
		}

		numFound++
		found[h.Name] = true

		buf, err := ioutil.ReadAll(tr)
		assert.NilError(t, err)
		assert.Check(t, is.Equal(string(buf), expected))

		if numFound == len(expect) {
			break
		}
	}

	assert.Check(t, found["/foo"], "/foo file not found in archive")
	assert.Check(t, found["/bar/baz"], "/bar/baz file not found in archive")
}
<|endoftext|>