mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	Merge pull request #90 from abiosoft/master
Git: More tests. Code refactor.
This commit is contained in:
		
						commit
						ee059c0910
					
				@ -2,7 +2,6 @@ package setup
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
@ -22,22 +21,8 @@ func Git(c *Controller) (middleware.Middleware, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.Startup = append(c.Startup, func() error {
 | 
						c.Startup = append(c.Startup, func() error {
 | 
				
			||||||
		// Startup functions are blocking; start
 | 
							// Start service routine in background
 | 
				
			||||||
		// service routine in background
 | 
							git.Start(repo)
 | 
				
			||||||
		go func() {
 | 
					 | 
				
			||||||
			for {
 | 
					 | 
				
			||||||
				time.Sleep(repo.Interval)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				err := repo.Pull()
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					if git.Logger == nil {
 | 
					 | 
				
			||||||
						log.Println(err)
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						git.Logger.Println(err)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Do a pull right away to return error
 | 
							// Do a pull right away to return error
 | 
				
			||||||
		return repo.Pull()
 | 
							return repo.Pull()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,9 @@
 | 
				
			|||||||
package setup
 | 
					package setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,18 +16,83 @@ func init() {
 | 
				
			|||||||
	git.SetOS(gittest.FakeOS)
 | 
						git.SetOS(gittest.FakeOS)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func check(t *testing.T, err error) {
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no errors, but got: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestGit(t *testing.T) {
 | 
					func TestGit(t *testing.T) {
 | 
				
			||||||
	c := newTestController(`git git@github.com:mholt/caddy.git`)
 | 
						c := newTestController(`git git@github.com:mholt/caddy.git`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mid, err := Git(c)
 | 
						mid, err := Git(c)
 | 
				
			||||||
	if err != nil {
 | 
						check(t, err)
 | 
				
			||||||
		t.Errorf("Expected no errors, but got: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if mid != nil {
 | 
						if mid != nil {
 | 
				
			||||||
		t.Fatal("Git middleware is a background service and expected to be nil.")
 | 
							t.Fatal("Git middleware is a background service and expected to be nil.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestIntervals(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []string{
 | 
				
			||||||
 | 
							`git git@github.com:user/repo { interval 10 }`,
 | 
				
			||||||
 | 
							`git git@github.com:user/repo { interval 5 }`,
 | 
				
			||||||
 | 
							`git git@github.com:user/repo { interval 2 }`,
 | 
				
			||||||
 | 
							`git git@github.com:user/repo { interval 1 }`,
 | 
				
			||||||
 | 
							`git git@github.com:user/repo { interval 6 }`,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, test := range tests {
 | 
				
			||||||
 | 
							git.Logger = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c1 := newTestController(test)
 | 
				
			||||||
 | 
							repo, err := gitParse(c1)
 | 
				
			||||||
 | 
							check(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c2 := newTestController(test)
 | 
				
			||||||
 | 
							_, err = Git(c2)
 | 
				
			||||||
 | 
							check(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// start startup services
 | 
				
			||||||
 | 
							err = c2.Startup[0]()
 | 
				
			||||||
 | 
							check(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// wait for first background pull
 | 
				
			||||||
 | 
							time.Sleep(time.Millisecond * 100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// switch logger to test file
 | 
				
			||||||
 | 
							logFile := gittest.Open("file")
 | 
				
			||||||
 | 
							git.Logger = log.New(logFile, "", 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// sleep for the interval
 | 
				
			||||||
 | 
							time.Sleep(repo.Interval)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// get log output
 | 
				
			||||||
 | 
							out, err := ioutil.ReadAll(logFile)
 | 
				
			||||||
 | 
							check(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// if greater than minimum interval
 | 
				
			||||||
 | 
							if repo.Interval >= time.Second*5 {
 | 
				
			||||||
 | 
								expected := `https://github.com/user/repo.git pulled.
 | 
				
			||||||
 | 
					No new changes.`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// ensure pull is done by tracing the output
 | 
				
			||||||
 | 
								if expected != strings.TrimSpace(string(out)) {
 | 
				
			||||||
 | 
									t.Errorf("Test %v: Expected %v found %v", i, expected, string(out))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// ensure pull is ignored by confirming no output
 | 
				
			||||||
 | 
								if string(out) != "" {
 | 
				
			||||||
 | 
									t.Errorf("Test %v: Expected no output but found %v", i, string(out))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// stop background thread monitor
 | 
				
			||||||
 | 
							git.Monitor.StopAndWait(repo.URL, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestGitParse(t *testing.T) {
 | 
					func TestGitParse(t *testing.T) {
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		input     string
 | 
							input     string
 | 
				
			||||||
 | 
				
			|||||||
@ -33,6 +33,9 @@ var initMutex = sync.Mutex{}
 | 
				
			|||||||
// Logger is used to log errors; if nil, the default log.Logger is used.
 | 
					// Logger is used to log errors; if nil, the default log.Logger is used.
 | 
				
			||||||
var Logger *log.Logger
 | 
					var Logger *log.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Monitor listens for halt signal to stop repositories from auto pulling.
 | 
				
			||||||
 | 
					var Monitor = &monitor{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// logger is an helper function to retrieve the available logger
 | 
					// logger is an helper function to retrieve the available logger
 | 
				
			||||||
func logger() *log.Logger {
 | 
					func logger() *log.Logger {
 | 
				
			||||||
	if Logger == nil {
 | 
						if Logger == nil {
 | 
				
			||||||
 | 
				
			|||||||
@ -139,6 +139,39 @@ Command echo Hello successful.
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// timeout checks
 | 
				
			||||||
 | 
						timeoutTests := []struct {
 | 
				
			||||||
 | 
							repo       *Repo
 | 
				
			||||||
 | 
							shouldPull bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{&Repo{Interval: time.Millisecond * 4900}, false},
 | 
				
			||||||
 | 
							{&Repo{Interval: time.Millisecond * 1}, false},
 | 
				
			||||||
 | 
							{&Repo{Interval: time.Second * 5}, true},
 | 
				
			||||||
 | 
							{&Repo{Interval: time.Second * 10}, true},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, r := range timeoutTests {
 | 
				
			||||||
 | 
							r.repo = createRepo(r.repo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := r.repo.Prepare()
 | 
				
			||||||
 | 
							check(t, err)
 | 
				
			||||||
 | 
							err = r.repo.Pull()
 | 
				
			||||||
 | 
							check(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							before := r.repo.lastPull
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							time.Sleep(r.repo.Interval)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = r.repo.Pull()
 | 
				
			||||||
 | 
							after := r.repo.lastPull
 | 
				
			||||||
 | 
							check(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							expected := after.After(before)
 | 
				
			||||||
 | 
							if expected != r.shouldPull {
 | 
				
			||||||
 | 
								t.Errorf("Pull with Error %v: Expected %v found %v", i, expected, r.shouldPull)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func createRepo(r *Repo) *Repo {
 | 
					func createRepo(r *Repo) *Repo {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										110
									
								
								middleware/git/service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								middleware/git/service.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										60
									
								
								middleware/git/service_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								middleware/git/service_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mholt/caddy/middleware/git/gittest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						SetOS(gittest.FakeOS)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Test(t *testing.T) {
 | 
				
			||||||
 | 
						repo := &Repo{URL: "git@github.com", Interval: time.Second}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Start(repo)
 | 
				
			||||||
 | 
						if len(Monitor.services) != 1 {
 | 
				
			||||||
 | 
							t.Errorf("Expected 1 service, found %v", len(Monitor.services))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Monitor.StopAndWait(repo.URL, 1)
 | 
				
			||||||
 | 
						if len(Monitor.services) != 0 {
 | 
				
			||||||
 | 
							t.Errorf("Expected 1 service, found %v", len(Monitor.services))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repos := make([]*Repo, 5)
 | 
				
			||||||
 | 
						for i := 0; i < 5; i++ {
 | 
				
			||||||
 | 
							repos[i] = &Repo{URL: fmt.Sprintf("test%v", i), Interval: time.Second * 2}
 | 
				
			||||||
 | 
							Start(repos[i])
 | 
				
			||||||
 | 
							if len(Monitor.services) != i+1 {
 | 
				
			||||||
 | 
								t.Errorf("Expected %v service(s), found %v", i+1, len(Monitor.services))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						time.Sleep(time.Second * 5)
 | 
				
			||||||
 | 
						Monitor.StopAndWait(repos[0].URL, 1)
 | 
				
			||||||
 | 
						if len(Monitor.services) != 4 {
 | 
				
			||||||
 | 
							t.Errorf("Expected %v service(s), found %v", 4, len(Monitor.services))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo = &Repo{URL: "git@github.com", Interval: time.Second}
 | 
				
			||||||
 | 
						Start(repo)
 | 
				
			||||||
 | 
						if len(Monitor.services) != 5 {
 | 
				
			||||||
 | 
							t.Errorf("Expected %v service(s), found %v", 5, len(Monitor.services))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo = &Repo{URL: "git@github.com", Interval: time.Second * 2}
 | 
				
			||||||
 | 
						Start(repo)
 | 
				
			||||||
 | 
						if len(Monitor.services) != 6 {
 | 
				
			||||||
 | 
							t.Errorf("Expected %v service(s), found %v", 6, len(Monitor.services))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						time.Sleep(time.Second * 5)
 | 
				
			||||||
 | 
						Monitor.StopAndWait(repo.URL, -1)
 | 
				
			||||||
 | 
						if len(Monitor.services) != 4 {
 | 
				
			||||||
 | 
							t.Errorf("Expected %v service(s), found %v", 4, len(Monitor.services))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user