caddy/middleware/git/service.go
2015-05-26 04:44:47 +01:00

111 lines
2.4 KiB
Go

package git
import (
"sync"
"time"
)
// RepoService is the repository service that runs in background and
// periodic pull from the repository.
type RepoService struct {
repo *Repo
running bool // whether service is running.
halt chan struct{} // channel to notify service to halt and stop pulling.
exit chan struct{} // channel to notify on exit.
}
// Start starts a new RepoService in background and adds it to monitor.
func Start(repo *Repo) {
service := &RepoService{
repo,
true,
make(chan struct{}),
make(chan struct{}),
}
// start service
go func(s *RepoService) {
for {
// if service is halted
if !s.running {
// notify exit channel
service.exit <- struct{}{}
break
}
time.Sleep(repo.Interval)
err := repo.Pull()
if err != nil {
logger().Println(err)
}
}
}(service)
// add to monitor to enable halting
Monitor.add(service)
}
// monitor monitors running services (RepoService)
// and can halt them.
type monitor struct {
services []*RepoService
sync.Mutex
}
// add adds a new service to the monitor.
func (m *monitor) add(service *RepoService) {
m.Lock()
defer m.Unlock()
m.services = append(m.services, service)
// start a goroutine to listen for halt signal
service.running = true
go func(r *RepoService) {
<-r.halt
r.running = false
}(service)
}
// Stop stops at most `limit` currently running services that is pulling from git repo at
// repoURL. It returns list of exit channels for the services. A wait for message on the
// channels guarantees exit. If limit is less than zero, it is ignored.
// TODO find better ways to identify repos
func (m *monitor) Stop(repoURL string, limit int) []chan struct{} {
m.Lock()
defer m.Unlock()
var chans []chan struct{}
// locate services
for i, j := 0, 0; i < len(m.services) && ((limit >= 0 && j < limit) || limit < 0); i++ {
s := m.services[i]
if s.repo.URL == repoURL {
// send halt signal
s.halt <- struct{}{}
chans = append(chans, s.exit)
j++
m.services[i] = nil
}
}
// remove them from services list
services := m.services[:0]
for _, s := range m.services {
if s != nil {
services = append(services, s)
}
}
m.services = services
return chans
}
// StopAndWait is similar to stop but it waits for the services to terminate before
// returning.
func (m *monitor) StopAndWait(repoUrl string, limit int) {
chans := m.Stop(repoUrl, limit)
for _, c := range chans {
<-c
}
}