diff options
Diffstat (limited to 'internal/service/service.go')
-rw-r--r-- | internal/service/service.go | 195 |
1 files changed, 0 insertions, 195 deletions
diff --git a/internal/service/service.go b/internal/service/service.go deleted file mode 100644 index c34bcd3..0000000 --- a/internal/service/service.go +++ /dev/null @@ -1,195 +0,0 @@ -package service - -import ( - "encoding/json" - "errors" - "fmt" - "math/rand" - "net/url" - "os" - "strconv" - "strings" - "sync" - "time" - - "git.server.ky/slackcoder/mirror/internal/github" -) - -type Mirror struct { - Method string `json:"method"` - From *url.URL `json:"-"` - To *url.URL `json:"-"` - Description string `json:"description,omitempty"` -} - -func (m *Mirror) Equal(arg *Mirror) bool { - return m.Method == arg.Method && m.From.String() == arg.From.String() && m.To.String() == arg.To.String() -} - -func (m *Mirror) String() string { - buf, err := json.Marshal(m) - if err != nil { - panic(err) - } - return string(buf) -} - -type Service struct { - cfg *Config - - mirrorsLock sync.Mutex - mirrors []*Mirror - schedule []time.Time - - stopCh chan struct{} -} - -func NewService(cfg *Config) (*Service, error) { - cfg.Apply(DefaultConfig) - - srv := &Service{ - cfg: cfg, - mirrors: nil, - stopCh: make(chan struct{}), - } - - for _, m := range cfg.Mirrors { - err := srv.AddMirror(m) - if err != nil { - return nil, err - } - } - - return srv, nil -} - -func (s *Service) scheduleNextRun(arg *Mirror) { - // Be polite. - period := s.cfg.MaxInterval.Duration - s.cfg.MinInterval.Duration - at := time.Now().Add(s.cfg.MinInterval.Duration + time.Duration(rand.Intn(int(period)))) - - for i, m := range s.mirrors { - if m.Equal(arg) { - s.schedule[i] = at - } - } -} - -func (s *Service) AddMirror(arg *Mirror) error { - defer s.mirrorsLock.Unlock() - s.mirrorsLock.Lock() - - for _, m := range s.mirrors { - if m.Equal(arg) { - return errors.New("must be unique") - } - } - - s.schedule = append(s.schedule, time.Time{}) - s.mirrors = append(s.mirrors, arg) - s.scheduleNextRun(arg) - - return nil -} - -func (s *Service) scheduled(yield func(*Mirror) bool) { - defer s.mirrorsLock.Unlock() - s.mirrorsLock.Lock() - - now := time.Now() - for i, m := range s.mirrors { - if s.schedule[i].After(now) { - continue - } - - if !yield(m) { - break - } - } -} - -func (s *Service) Mirror(arg *Mirror) error { - if arg.From.Path == "" || arg.To.Path == "" || arg.Method == "" { - return fmt.Errorf("badly formatted mirror '%s'", arg.String()) - } - - var err error - - switch arg.Method { - case "git": - err = MirrorGit(arg.To, arg.From, arg.Description) - case "github-assets": - client := github.NewClient() - err = client.MirrorAssets(arg.To, arg.From) - case "rsync": - err = Rsync(arg.To, arg.From) - default: - err = fmt.Errorf("unknown method '%s'", arg.Method) - } - - if err != nil { - return fmt.Errorf("could not clone from '%s': %w", arg.From, err) - } - - return nil -} - -func (s *Service) RemoveMirror(arg *Mirror) error { - defer s.mirrorsLock.Unlock() - s.mirrorsLock.Lock() - - for i, m := range s.mirrors { - if m.Equal(arg) { - s.mirrors[i] = s.mirrors[len(s.mirrors)-1] - s.mirrors = s.mirrors[:len(s.mirrors)-1] - - s.schedule[i] = s.schedule[len(s.schedule)-1] - s.schedule = s.schedule[:len(s.schedule)-1] - - return nil - } - } - - return errors.New("not found") -} - -func (s *Service) Log(str string) { - escaped := []rune(strconv.Quote(strings.TrimSpace(str))) - escaped = escaped[1 : len(escaped)-1] - - fmt.Fprintf(os.Stderr, "%s: %s\n", time.Now().Format(time.RFC822Z), string(escaped)) -} - -func (s *Service) Run() error { - wakeup := time.NewTicker(time.Second) - -mainLoop: - for { - select { - case <-wakeup.C: - case <-s.stopCh: - break mainLoop - } - - s.scheduled(func(m *Mirror) bool { - err := s.Mirror(m) - if err != nil { - s.Log(err.Error()) - } - - s.scheduleNextRun(m) - - return true - }) - } - - return nil -} - -func (s *Service) Stop() { - select { - case <-s.stopCh: - default: - close(s.stopCh) - } -} |