github.com/Cloud-Foundations/Dominator@v0.3.4/lib/url/urlutil/watchUrl.go (about)

     1  package urlutil
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/url"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
    15  	"github.com/Cloud-Foundations/Dominator/lib/gitutil"
    16  	"github.com/Cloud-Foundations/Dominator/lib/log"
    17  )
    18  
    19  const (
    20  	driverFile = iota
    21  	driverGit
    22  	driverHttp
    23  )
    24  
    25  type driverDataType struct {
    26  	driverType uint
    27  	gitUrl     string
    28  	pathname   string
    29  	rawUrl     string
    30  }
    31  
    32  func parseUrl(rawUrl string) (*driverDataType, error) {
    33  	if strings.HasPrefix(rawUrl, "git@") {
    34  		pos := strings.Index(rawUrl, ".git/")
    35  		if pos < 5 {
    36  			return nil, errors.New("missing .git/ in Git URL")
    37  		}
    38  		if pos+5 >= len(rawUrl) {
    39  			return nil, errors.New("missing path in repository")
    40  		}
    41  		return &driverDataType{
    42  			driverType: driverGit,
    43  			gitUrl:     rawUrl[:pos+4],
    44  			pathname:   rawUrl[pos+5:],
    45  		}, nil
    46  	}
    47  	if rawUrl[0] == '/' {
    48  		return &driverDataType{
    49  			driverType: driverFile,
    50  			pathname:   rawUrl,
    51  		}, nil
    52  	}
    53  	u, err := url.Parse(rawUrl)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	if u.Scheme == "file" {
    58  		return &driverDataType{
    59  			driverType: driverFile,
    60  			pathname:   u.Path,
    61  		}, nil
    62  	}
    63  	if u.Scheme == "http" || u.Scheme == "https" {
    64  		if strings.HasSuffix(u.Path, ".git") {
    65  			if len(u.RawQuery) < 1 {
    66  				return nil, errors.New("missing path in repository")
    67  			}
    68  			return &driverDataType{
    69  				driverType: driverGit,
    70  				gitUrl:     u.Scheme + "://" + u.Host + u.Path,
    71  				pathname:   u.RawQuery,
    72  			}, nil
    73  		}
    74  		return &driverDataType{
    75  			driverType: driverHttp,
    76  			rawUrl:     rawUrl,
    77  		}, nil
    78  	}
    79  	return nil, errors.New("unknown scheme: " + u.Scheme)
    80  }
    81  
    82  func watchUrl(rawUrl string, checkInterval time.Duration,
    83  	logger log.DebugLogger) (<-chan io.ReadCloser, error) {
    84  	driverData, err := parseUrl(rawUrl)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	switch driverData.driverType {
    89  	case driverFile:
    90  		return fsutil.WatchFile(driverData.pathname, logger), nil
    91  	case driverGit:
    92  		ch := make(chan io.ReadCloser, 1)
    93  		go watchGitLoop(driverData.gitUrl, driverData.pathname, checkInterval,
    94  			ch, logger)
    95  		return ch, nil
    96  	case driverHttp:
    97  		ch := make(chan io.ReadCloser, 1)
    98  		go watchUrlLoop(driverData.rawUrl, checkInterval, ch, logger)
    99  		return ch, nil
   100  	}
   101  	return nil, errors.New("unknown driver")
   102  }
   103  
   104  func watchGitLoop(gitUrl, pathInRepo string, checkInterval time.Duration,
   105  	ch chan<- io.ReadCloser, logger log.DebugLogger) {
   106  	for ; ; time.Sleep(checkInterval) {
   107  		watchGitOnce(gitUrl, pathInRepo, ch, logger)
   108  		if checkInterval <= 0 {
   109  			return
   110  		}
   111  	}
   112  }
   113  
   114  func watchGitOnce(gitUrl, pathInRepo string, ch chan<- io.ReadCloser,
   115  	logger log.DebugLogger) {
   116  	topdir, err := ioutil.TempDir("", "watchGitOnce")
   117  	if err != nil {
   118  		logger.Println(err)
   119  		return
   120  	}
   121  	defer os.RemoveAll(topdir)
   122  	err = gitutil.ShallowClone(topdir, gitutil.ShallowCloneParams{
   123  		Patterns: []string{pathInRepo},
   124  		RepoURL:  gitUrl,
   125  	}, logger)
   126  	if err != nil {
   127  		logger.Println(err)
   128  		return
   129  	}
   130  	filename := filepath.Join(topdir, pathInRepo)
   131  	if file, err := os.Open(filename); err != nil {
   132  		logger.Println(err)
   133  		return
   134  	} else {
   135  		ch <- file
   136  	}
   137  }
   138  
   139  func watchUrlLoop(rawUrl string, checkInterval time.Duration,
   140  	ch chan<- io.ReadCloser, logger log.Logger) {
   141  	for ; ; time.Sleep(checkInterval) {
   142  		watchUrlOnce(rawUrl, ch, logger)
   143  		if checkInterval <= 0 {
   144  			return
   145  		}
   146  	}
   147  }
   148  
   149  func watchUrlOnce(rawUrl string, ch chan<- io.ReadCloser, logger log.Logger) {
   150  	resp, err := http.Get(rawUrl)
   151  	if err != nil {
   152  		logger.Println(err)
   153  		return
   154  	}
   155  	if resp.StatusCode != http.StatusOK {
   156  		logger.Println(resp.Status)
   157  		return
   158  	}
   159  	ch <- resp.Body
   160  }