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 }