-
Notifications
You must be signed in to change notification settings - Fork 14
Fix #223 Honour cluster-capacity-exhausted flag when unidling Jenkins #227
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,15 +2,18 @@ package api | |
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"strings" | ||
"time" | ||
|
||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/cluster" | ||
pidler "github.com/fabric8-services/fabric8-jenkins-idler/internal/idler" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/model" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/openshift" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/openshift/client" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/tenant" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/metric" | ||
"github.com/julienschmidt/httprouter" | ||
log "github.com/sirupsen/logrus" | ||
|
@@ -54,20 +57,22 @@ type idler struct { | |
clusterView cluster.View | ||
openShiftClient client.OpenShiftClient | ||
controller openshift.Controller | ||
tenantService tenant.Service | ||
} | ||
|
||
type status struct { | ||
IsIdle bool `json:"is_idle"` | ||
} | ||
|
||
// NewIdlerAPI creates a new instance of IdlerAPI. | ||
func NewIdlerAPI(userIdlers *openshift.UserIdlerMap, clusterView cluster.View) IdlerAPI { | ||
func NewIdlerAPI(userIdlers *openshift.UserIdlerMap, clusterView cluster.View, ts tenant.Service) IdlerAPI { | ||
// Initialize metrics | ||
Recorder.Initialize() | ||
return &idler{ | ||
userIdlers: userIdlers, | ||
clusterView: clusterView, | ||
openShiftClient: client.NewOpenShift(), | ||
tenantService: ts, | ||
} | ||
} | ||
|
||
|
@@ -100,23 +105,52 @@ func (api *idler) Idle(w http.ResponseWriter, r *http.Request, ps httprouter.Par | |
} | ||
|
||
func (api *idler) UnIdle(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { | ||
openShiftAPI, openShiftBearerToken, err := api.getURLAndToken(r) | ||
|
||
openshiftURL, openshiftToken, err := api.getURLAndToken(r) | ||
if err != nil { | ||
log.Error(err) | ||
w.WriteHeader(http.StatusBadRequest) | ||
w.Write([]byte(fmt.Sprintf("{\"error\": \"%s\"}", err))) | ||
respondWithError(w, http.StatusBadRequest, err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whynot log the error as well? If the consumer does not log the exception, nor show to the user, we will not be able to know what's happening, isn't it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nvm my previous comment |
||
return | ||
} | ||
|
||
ns := strings.TrimSpace(ps.ByName("namespace")) | ||
if ns == "" { | ||
err = errors.New("Missing mandatory param namespace") | ||
respondWithError(w, http.StatusBadRequest, err) | ||
return | ||
} | ||
|
||
// may be jenkins is already running and in that case we don't have to do unidle it | ||
running, err := api.isJenkinsUnIdled(openshiftURL, openshiftToken, ns) | ||
if err != nil { | ||
respondWithError(w, http.StatusInternalServerError, err) | ||
return | ||
} else if running { | ||
log.Infof("Jenkins is already starting/running on %s", ns) | ||
w.WriteHeader(http.StatusOK) | ||
return | ||
} | ||
|
||
// now that jenkins isn't running we need to check if the cluster has reached | ||
// its maximum capacity | ||
clusterFull, err := api.tenantService.HasReachedMaxCapacity(openshiftURL, ns) | ||
if err != nil { | ||
respondWithError(w, http.StatusInternalServerError, err) | ||
return | ||
} else if clusterFull { | ||
err := fmt.Errorf("Maximum Resource limit reached on %s for %s", openshiftURL, ns) | ||
respondWithError(w, http.StatusServiceUnavailable, err) | ||
return | ||
} | ||
|
||
// unidle now | ||
for _, service := range pidler.JenkinsServices { | ||
startTime := time.Now() | ||
err = api.openShiftClient.UnIdle(openShiftAPI, openShiftBearerToken, ps.ByName("namespace"), service) | ||
|
||
err = api.openShiftClient.UnIdle(openshiftURL, openshiftToken, ns, service) | ||
elapsedTime := time.Since(startTime).Seconds() | ||
if err != nil { | ||
log.Error(err) | ||
Recorder.RecordReqDuration(service, "UnIdle", http.StatusInternalServerError, elapsedTime) | ||
w.WriteHeader(http.StatusInternalServerError) | ||
w.Write([]byte(fmt.Sprintf("{\"error\": \"%s\"}", err))) | ||
respondWithError(w, http.StatusInternalServerError, err) | ||
return | ||
} | ||
|
||
|
@@ -190,3 +224,19 @@ func (api *idler) getURLAndToken(r *http.Request) (string, string, error) { | |
} | ||
return "", "", fmt.Errorf("Unknown or invalid OpenShift API URL") | ||
} | ||
|
||
func (api idler) isJenkinsUnIdled(openshiftURL, openshiftToken, namespace string) (bool, error) { | ||
state, err := api.openShiftClient.IsIdle(openshiftURL, openshiftToken, namespace, "jenkins") | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
status := state == model.JenkinsStarting || state == model.JenkinsRunning | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sthaha Eventhough it looks good to me, let's be sure about the precedence here https://golang.org/ref/spec#Operator_precedence Is go-format getting rid of brackets here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kishansagathiya I didn't put brackets because the precedence is clear to me and tbh we don't need to make any simpler ... If there is guideline I am happy to follow it but that is beyond the scope of this change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cool then |
||
return status, nil | ||
} | ||
|
||
func respondWithError(w http.ResponseWriter, status int, err error) { | ||
log.Error(err) | ||
w.WriteHeader(status) | ||
w.Write([]byte(fmt.Sprintf("{\"error\": \"%s\"}", err))) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why
BadRequestError
status and notInternalServerError
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be outside the scope of the this commit. The commit does not try to modify the existing behaviour, thus
BadRequest