Skip to content

Commit

Permalink
Issue #179 Split handleJenkinsUIRequest() into smaller methods
Browse files Browse the repository at this point in the history
  • Loading branch information
kishansagathiya committed Mar 16, 2018
1 parent 187decc commit 1eba87e
Showing 1 changed file with 98 additions and 71 deletions.
169 changes: 98 additions & 71 deletions internal/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,165 +184,189 @@ func (p *Proxy) Handle(w http.ResponseWriter, r *http.Request) {
}).ServeHTTP(w, r)
}

func (p *Proxy) handleJenkinsUIRequest(w http.ResponseWriter, r *http.Request, requestLogEntry *log.Entry) (cacheKey string, ns string, noProxy bool) {
func (p *Proxy) handleJenkinsUIRequest(responseWriter http.ResponseWriter, request *http.Request, requestLogEntry *log.Entry) (cacheKey string, namespace string, noProxy bool) {
//redirect determines if we need to redirect to auth service
needsAuth := true
noProxy = true

//redirectURL is used for auth service as a target of successful auth redirect
redirectURL, err := url.ParseRequestURI(fmt.Sprintf("%s%s", strings.TrimRight(p.redirect, "/"), r.URL.Path))
// redirectURL is used for auth service as a target of successful auth redirect
redirectURL, err := url.ParseRequestURI(fmt.Sprintf("%s%s", strings.TrimRight(p.redirect, "/"), request.URL.Path))
if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
return
}

//If the user provides OSO token, we can directly proxy
if _, ok := r.Header["Authorization"]; ok { //FIXME Do we need this?
if _, ok := request.Header["Authorization"]; ok { //FIXME Do we need this?
needsAuth = false
}

if tj, ok := r.URL.Query()["token_json"]; ok { //If there is token_json in query, process it, find user info and login to Jenkins
if len(tj) < 1 {
p.HandleError(w, errors.New("could not read JWT token from URL"), requestLogEntry)
namespace, noProxy = p.processTokenJSON(responseWriter, request, requestLogEntry, redirectURL)

cacheKey, namespace, noProxy, needsAuth = p.checkCookies(responseWriter, request, requestLogEntry)

//Check if we need to redirect to auth service
if needsAuth {
redirAuth := GetAuthURI(p.authURL, redirectURL.String())
requestLogEntry.Infof("Redirecting to auth: %s", redirAuth)
http.Redirect(responseWriter, request, redirAuth, 301)
}
return
}

// processTokenJSON processes token_json if present in query and find user info and login to Jenkins
func (p *Proxy) processTokenJSON(responseWriter http.ResponseWriter, request *http.Request, requestLogEntry *log.Entry, redirectURL *url.URL) (namespace string, noProxy bool) {

// Checks if token_json is present in the query
if tokenJSON, ok := request.URL.Query()["token_json"]; ok {
if len(tokenJSON) < 1 {
p.HandleError(responseWriter, errors.New("could not read JWT token from URL"), requestLogEntry)
return
}

pci, osioToken, err := p.processToken([]byte(tj[0]), requestLogEntry)
proxyCacheItem, osioToken, err := p.processToken([]byte(tokenJSON[0]), requestLogEntry)
if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
return
}
ns = pci.NS
clusterURL := pci.ClusterURL
requestLogEntry.WithFields(log.Fields{"ns": ns, "cluster": clusterURL}).Debug("Found token info in query")

isIdle, err := p.idler.IsIdle(ns, clusterURL)
namespace = proxyCacheItem.NS
clusterURL := proxyCacheItem.ClusterURL
requestLogEntry.WithField("ns", namespace).Debug("Found token info in query")

// Check if jenkins is idled
isIdle, err := p.idler.IsIdle(namespace, clusterURL)
if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
return
}

//Break the process if the Jenkins is idled, set a cookie and redirect to self
// Break the process if the Jenkins is idled, set a cookie and redirect to self
if isIdle {
err = p.idler.UnIdle(ns, clusterURL)
// Initiates unidling of the Jenkins instance
err = p.idler.UnIdle(namespace, clusterURL)
if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
return
}

p.setIdledCookie(w, pci)
requestLogEntry.WithField("ns", ns).Info("Redirecting to remove token from URL")
http.Redirect(w, r, redirectURL.String(), http.StatusFound) //Redirect to get rid of token in URL
// sets a cookie, in which cookie value is id of proxyCacheItem in cache
p.setIdledCookie(responseWriter, proxyCacheItem)
requestLogEntry.WithField("ns", namespace).Info("Redirecting to remove token from URL")
http.Redirect(responseWriter, request, redirectURL.String(), http.StatusFound) //Redirect to get rid of token in URL
return
}

osoToken, err := GetOSOToken(p.authURL, pci.ClusterURL, osioToken)
// gets openshift online token
osoToken, err := GetOSOToken(p.authURL, clusterURL, osioToken)
if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
return
}
requestLogEntry.WithField("ns", ns).Debug("Loaded OSO token")
requestLogEntry.WithField("ns", namespace).Debug("Loaded OSO token")

statusCode, cookies, err := p.loginJenkins(pci, osoToken, requestLogEntry)
// login to Jenkins and gets cookies
statusCode, cookies, err := p.loginJenkins(proxyCacheItem, osoToken, requestLogEntry)
if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
return
}
if statusCode == http.StatusOK {
// sets all cookies that we got from loginJenkins method
for _, cookie := range cookies {
// No need to set a cookie for whether jenkins if idled, because we have already done that
if cookie.Name == CookieJenkinsIdled {
continue
}
http.SetCookie(w, cookie)
if strings.HasPrefix(cookie.Name, SessionCookie) { //Find session cookie and use it's value as a key for cache
p.ProxyCache.SetDefault(cookie.Value, pci)
requestLogEntry.WithField("ns", ns).Infof("Cached Jenkins route %s in %s", pci.Route, cookie.Value)
requestLogEntry.WithField("ns", ns).Infof("Redirecting to %s", redirectURL.String())
http.SetCookie(responseWriter, cookie)
if strings.HasPrefix(cookie.Name, SessionCookie) {
// Find session cookie and use it's value as a key for cache
p.ProxyCache.SetDefault(cookie.Value, proxyCacheItem)
requestLogEntry.WithField("ns", namespace).Infof("Cached Jenkins route %s in %s", proxyCacheItem.Route, cookie.Value)
requestLogEntry.WithField("ns", namespace).Infof("Redirecting to %s", redirectURL.String())
//If all good, redirect to self to remove token from url
http.Redirect(w, r, redirectURL.String(), http.StatusFound)
http.Redirect(responseWriter, request, redirectURL.String(), http.StatusFound)
return
}

//If we got here, the cookie was not found - report error
p.HandleError(w, fmt.Errorf("could not find cookie %s for %s", SessionCookie, pci.NS), requestLogEntry)
p.HandleError(responseWriter, fmt.Errorf("could not find cookie %s for %s", SessionCookie, proxyCacheItem.NS), requestLogEntry)
}
} else {
p.HandleError(w, fmt.Errorf("could not login to Jenkins in %s namespace", ns), requestLogEntry)
p.HandleError(responseWriter, fmt.Errorf("could not login to Jenkins in %s namespace", namespace), requestLogEntry)
}
}
return
}

// checkCookies checks cookies and proxy cache to find user info
func (p *Proxy) checkCookies(responseWriter http.ResponseWriter, request *http.Request, requestLogEntry *log.Entry) (cacheKey string, namespace string, noProxy bool, needsAuth bool) {

if len(r.Cookies()) > 0 { //Check cookies and proxy cache to find user info
for _, cookie := range r.Cookies() {
if len(request.Cookies()) > 0 {
for _, cookie := range request.Cookies() {
cacheVal, ok := p.ProxyCache.Get(cookie.Value)
if !ok {
continue
}
if strings.HasPrefix(cookie.Name, SessionCookie) { //We found a session cookie in cache
if strings.HasPrefix(cookie.Name, SessionCookie) { // We found a session cookie in cache
cacheKey = cookie.Value
pci := cacheVal.(ProxyCacheItem)
r.Host = pci.Route //Configure proxy upstream
r.URL.Host = pci.Route
r.URL.Scheme = pci.Scheme
ns = pci.NS
needsAuth = false //user is probably logged in, do not redirect
proxyCacheItem := cacheVal.(ProxyCacheItem)
request.Host = proxyCacheItem.Route // Configure proxy upstream
request.URL.Host = proxyCacheItem.Route
request.URL.Scheme = proxyCacheItem.Scheme
namespace = proxyCacheItem.NS
needsAuth = false // user is logged in, do not redirect
noProxy = false
break
} else if cookie.Name == CookieJenkinsIdled { //Found a cookie saying Jenkins is idled, verify and act accordingly
} else if cookie.Name == CookieJenkinsIdled { // Found a cookie saying Jenkins is idled, verify and act accordingly
cacheKey = cookie.Value
needsAuth = false
pci := cacheVal.(ProxyCacheItem)
ns = pci.NS
clusterURL := pci.ClusterURL
isIdle, err := p.idler.IsIdle(ns, clusterURL)
proxyCacheItem := cacheVal.(ProxyCacheItem)
namespace = proxyCacheItem.NS
clusterURL := proxyCacheItem.ClusterURL
isIdle, err := p.idler.IsIdle(proxyCacheItem.NS, clusterURL)
if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
return
}
if isIdle { //If jenkins is idled, return loading page and status 202
err = p.idler.UnIdle(ns, clusterURL)
err = p.idler.UnIdle(namespace, clusterURL)
if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
return
}
err = p.processTemplate(w, ns, requestLogEntry)
p.recordStatistics(pci.NS, time.Now().Unix(), 0) //FIXME - maybe do this at the beginning?
err = p.processTemplate(responseWriter, namespace, requestLogEntry)
p.recordStatistics(proxyCacheItem.NS, time.Now().Unix(), 0) //FIXME - maybe do this at the beginning?
} else { //If Jenkins is running, remove the cookie
//OpenShift can take up to couple tens of second to update HAProxy configuration for new route
//so even if the pod is up, route might still return 500 - i.e. we need to check the route
//before claiming Jenkins is up
var statusCode int
statusCode, _, err = p.loginJenkins(pci, "", requestLogEntry)
statusCode, _, err = p.loginJenkins(proxyCacheItem, "", requestLogEntry)
if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
return
}
if statusCode == 200 || statusCode == 403 {
cookie.Expires = time.Unix(0, 0)
http.SetCookie(w, cookie)
http.SetCookie(responseWriter, cookie)
} else {
err = p.processTemplate(w, ns, requestLogEntry)
err = p.processTemplate(responseWriter, namespace, requestLogEntry)
}
}

if err != nil {
p.HandleError(w, err, requestLogEntry)
p.HandleError(responseWriter, err, requestLogEntry)
}
break
}
}
if len(cacheKey) == 0 { //If we do not have user's info cached, run through login process to get it
requestLogEntry.WithField("ns", ns).Info("Could not find cache, redirecting to re-login")
requestLogEntry.WithField("ns", namespace).Info("Could not find cache, redirecting to re-login")
} else {
requestLogEntry.WithField("ns", ns).Infof("Found cookie %s", cacheKey)
requestLogEntry.WithField("ns", namespace).Infof("Found cookie %s", cacheKey)
}
}

//Check if we need to redirect to auth service
if needsAuth {
redirAuth := GetAuthURI(p.authURL, redirectURL.String())
requestLogEntry.Infof("Redirecting to auth: %s", redirAuth)
http.Redirect(w, r, redirAuth, 301)
}
return
}

Expand All @@ -365,12 +389,13 @@ func (p *Proxy) loginJenkins(pci ProxyCacheItem, osoToken string, requestLogEntr
return resp.StatusCode, resp.Cookies(), err
}

func (p *Proxy) setIdledCookie(w http.ResponseWriter, pci ProxyCacheItem) {
func (p *Proxy) setIdledCookie(w http.ResponseWriter, proxyCacheItem ProxyCacheItem) {
c := &http.Cookie{}
u1 := uuid.NewV4().String()
id := uuid.NewV4().String()
c.Name = CookieJenkinsIdled
c.Value = u1
p.ProxyCache.SetDefault(u1, pci)
c.Value = id
// Store proxyCacheItem at id in cache
p.ProxyCache.SetDefault(id, proxyCacheItem)
http.SetCookie(w, c)
return
}
Expand Down Expand Up @@ -479,6 +504,8 @@ func (p *Proxy) processTemplate(w http.ResponseWriter, ns string, requestLogEntr
return
}

// Extract openshift.io token (access_token from token_json)
// and get proxy cache item using openshift.io token
func (p *Proxy) processToken(tokenData []byte, requestLogEntry *log.Entry) (pci ProxyCacheItem, osioToken string, err error) {
tokenJSON := &TokenJSON{}
err = json.Unmarshal(tokenData, tokenJSON)
Expand Down

0 comments on commit 1eba87e

Please sign in to comment.