Skip to content
This repository has been archived by the owner on May 6, 2021. It is now read-only.

WIP: Add debug tools #279

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ all: tools build test fmtcheck vet lint validate_commits image ## Compiles binar
build: vendor $(AUTH_GEN_DIR)/*.go ## Builds the binary into $GOPATH/bin
go install -ldflags="$(LD_FLAGS)" ./cmd/fabric8-jenkins-idler

debug: vendor $(AUTH_GEN_DIR)/*.go ## Build and runs binary in debugger
go build -race -ldflags="$(LD_FLAGS)" ./cmd/fabric8-jenkins-idler
dlv debug ./cmd/fabric8-jenkins-idler

$(BUILD_DIR):
@mkdir $(BUILD_DIR)

Expand Down Expand Up @@ -70,6 +74,8 @@ tools.timestamp:
go get -u github.com/vbatts/git-validation/...
go get -u github.com/goadesign/goa/goagen
go get -u github.com/haya14busa/goverage
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Humm wanna use it somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it works locally quite well with syntastic and vim

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api.go

  type idler struct {                                                                                                                                                                                              
    userIdlers      *openshift.UserIdlerMap                                                                                                                                                                        
    clusterView     cluster.View                                                                                                                                                                                   
    openShiftClient client.OpenShiftClient                                                                                                                                                                         
✗   controller      openshift.Controller         // not used anywhere                                                                                                                                                                   
    tenantService   tenant.Service                                                                                                                                                                                 
  }                                                                                                                                                                                                                

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes right i mean in a makefile target like this for example :

https://github.com/fabric8-services/fabric8-build-service/blob/master/Makefile#L176

go get -u github.com/derekparker/delve/cmd/dlv
@touch tools.timestamp

vendor: tools.timestamp $(AUTH_GEN_DIR)/*.go ## Runs dep to vendor project dependencies
Expand Down
33 changes: 30 additions & 3 deletions internal/condition/build_condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/fabric8-services/fabric8-jenkins-idler/internal/model"
"github.com/sirupsen/logrus"
)

// BuildCondition covers builds a user has/had running.
Expand All @@ -28,26 +29,52 @@ func (c *BuildCondition) Eval(object interface{}) (bool, error) {
return false, fmt.Errorf("%T is not of type User", object)
}

log := logrus.WithFields(logrus.Fields{
"id": u.ID,
"name": u.Name,
"component": "build-condition",
})

if u.HasActiveBuilds() {
// if we have activebuild being active over x time then see it as
// expired or they would be lingering forever (i.e: approval process pipelines)
if u.ActiveBuild.Status.StartTimestamp.Time.Add(c.idleLongBuild).Before(time.Now()) {
startTime := u.ActiveBuild.Status.StartTimestamp.Time
if startTime.Add(c.idleLongBuild).Before(time.Now()) {
log.WithField("action", "idle").Infof(
"active build started at %v has exceeded timeout %v",
startTime, c.idleLongBuild)
return true, nil
}

if u.ActiveBuild.Status.Phase == "New" && u.ActiveBuild.Status.StartTimestamp.Time.Add(time.Second*5).After(u.ActiveBuild.Status.CompletionTimestamp.Time) {
completionTime := u.ActiveBuild.Status.CompletionTimestamp.Time
if u.ActiveBuild.Status.Phase == "New" &&
startTime.Add(time.Second*5).After(completionTime) {
log.WithField("action", "idle").Infof(
"active build started at %v has gone past completion time %v",
startTime, completionTime)
return true, nil
}

log.WithField("action", "unidle").Infof(
"active build started at %v seems to be in progress", startTime)
return false, nil
}

if !u.HasBuilds() {
log.WithField("action", "idle").Infof("user has no builds")
return true, nil
}

if u.DoneBuild.Status.CompletionTimestamp.Time.Add(c.idleAfter).Before(time.Now()) {
completionTime := u.DoneBuild.Status.CompletionTimestamp.Time
if completionTime.Add(c.idleAfter).Before(time.Now()) {
log.WithField("action", "idle").Infof(
"%v has elapsed after last done-build at %v ",
c.idleAfter, completionTime)
return true, nil
}

log.WithField("action", "unidle").Infof(
"%v has not yet elapsed after last done-build at %v ",
c.idleAfter, completionTime)
return false, nil
}
6 changes: 3 additions & 3 deletions internal/condition/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (c *Conditions) Eval(o interface{}) (bool, util.MultiError) {
condStates[name] = r
}

log.Debugf("Conditions: %t = %s", result, c.conditionMapToString(condStates))
log.Infof("Conditions: %t = %s", result, c.conditionMapToString(condStates))
return result, errors
}

Expand All @@ -57,7 +57,7 @@ func (c *Conditions) Add(name string, condition Condition) {
func (c *Conditions) conditionMapToString(conditions map[string]bool) string {
var result []string
for key, value := range conditions {
result = append(result, fmt.Sprintf("%s(%t)", key, value))
result = append(result, fmt.Sprintf("%s=%t", key, value))
}
return strings.Join(result, " ")
return strings.Join(result, " | ")
}
21 changes: 17 additions & 4 deletions internal/condition/deployment_config_condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/fabric8-services/fabric8-jenkins-idler/internal/model"
"github.com/sirupsen/logrus"
)

// DeploymentConfigCondition covers changes to DeploymentConfigs.
Expand All @@ -14,22 +15,34 @@ type DeploymentConfigCondition struct {

// NewDCCondition creates a new instance of DeploymentConfigCondition.
func NewDCCondition(idleAfter time.Duration) Condition {
b := &DeploymentConfigCondition{
return &DeploymentConfigCondition{
idleAfter: idleAfter,
}
return b
}

// Eval returns true if the last deployment config change occurred for more than the configured idle after interval.
func (c *DeploymentConfigCondition) Eval(object interface{}) (bool, error) {
b, ok := object.(model.User)
u, ok := object.(model.User)
if !ok {
return false, fmt.Errorf("%T is not of type User", object)
}

if b.JenkinsLastUpdate.Add(c.idleAfter).Before(time.Now()) {
log := logrus.WithFields(logrus.Fields{
"id": u.ID,
"name": u.Name,
"component": "dc-condition",
})

if u.JenkinsLastUpdate.Add(c.idleAfter).Before(time.Now()) {
log.WithField("action", "idle").Infof(
"%v has elapsed after jenkins last update at %v",
c.idleAfter, u.JenkinsLastUpdate)
return true, nil
}

log.WithField("action", "unidle").Infof(
"%v has not elapsed after jenkins last update at %v",
c.idleAfter, u.JenkinsLastUpdate)

return false, nil
}
51 changes: 33 additions & 18 deletions internal/idler/user_idler.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
log "github.com/sirupsen/logrus"
)

var logger = log.WithFields(log.Fields{"component": "user-idler"})
var logger = log.WithField("component", "user-idler")

// JenkinsServices is an array of all the services getting idled or unidled
// they go along the main build detection logic of jenkins and don't have
Expand Down Expand Up @@ -60,7 +60,7 @@ func NewUserIdler(

logEntry := log.WithFields(log.Fields{
"component": "user-idler",
"username": user.Name,
"name": user.Name,
"id": user.ID,
})
logEntry.Info("UserIdler created.")
Expand Down Expand Up @@ -100,35 +100,48 @@ func (idler *UserIdler) GetChannel() chan model.User {
// checkIdle verifies the state of conditions and decides if we should idle/unidle
// and performs the required action if needed.
func (idler *UserIdler) checkIdle() error {
eval, errors := idler.Conditions.Eval(idler.user)

enabled, err := idler.isIdlerEnabled()
if err != nil {
idler.logger.Errorf("Failed to check if idler is enabled for user: %s", err)
return err
}

if !enabled {
idler.logger.Warnf("idler disabled for user %s - skipping", idler.user.Name)
return nil
}

idler.logger.Infof("Evaluating conditions for user %s", idler.user.Name)

shouldIdle, errors := idler.Conditions.Eval(idler.user)
if !errors.Empty() {
idler.logger.Errorf("Failed to evaluate conditions for %s", idler.user.Name)
return errors.ToError()
}

idler.logger.WithField("eval", eval).Debug("Check idle state")
if eval {
enabled, err := idler.isIdlerEnabled()
if err != nil {
return err
}
if enabled {
err := idler.doIdle()
// TODO: find a better way to update IdleStatus inside doIdle()
idler.user.IdleStatus = model.NewIdleStatus(err)
}
idler.logger.WithField("idle-jenkins", shouldIdle).Infof("Check idle state %s", idler.user.ID)

if shouldIdle {
err := idler.doIdle()
// TODO: find a better way to update IdleStatus inside doIdle()
idler.user.IdleStatus = model.NewIdleStatus(err)
} else {
err := idler.doUnIdle()
// TODO: find a better way to update IdleStatus inside doUnIdle()
idler.user.IdleStatus = model.NewUnidleStatus(err)
}

return nil
}

// Run runs/starts the Idler
// It checks if Jenkins is idle at every checkIdle duration.
func (idler *UserIdler) Run(ctx context.Context, wg *sync.WaitGroup, cancel context.CancelFunc, checkIdle time.Duration, maxRetriesQuietInterval time.Duration) {
idler.logger.WithFields(log.Fields{"checkIdle": fmt.Sprintf("%.0fm", checkIdle.Minutes()), "maxRetriesQuietInterval": fmt.Sprintf("%.0fm", maxRetriesQuietInterval.Minutes())}).Info("UserIdler started.")
idler.logger.WithFields(log.Fields{
"checkIdle": fmt.Sprintf("%.0fm", checkIdle.Minutes()),
"maxRetriesQuietInterval": fmt.Sprintf("%.0fm", maxRetriesQuietInterval.Minutes()),
}).Info("UserIdler started.")

wg.Add(1)
go func() {
ticker := time.Tick(maxRetriesQuietInterval)
Expand Down Expand Up @@ -168,7 +181,7 @@ func (idler *UserIdler) Run(ctx context.Context, wg *sync.WaitGroup, cancel cont

func (idler *UserIdler) doIdle() error {
if idler.idleAttempts >= idler.maxRetries {
idler.logger.Warn("Skipping idle request since max retry count has been reached.")
idler.logger.Warn("Skipping idle request since max retry count %d has reached.", idler.maxRetries)
return nil
}

Expand Down Expand Up @@ -287,7 +300,9 @@ func createWatchConditions(proxyURL string, idleAfter int, idleLongBuild int, lo
conditions := condition.NewConditions()

// Add a Build condition.
conditions.Add("build", condition.NewBuildCondition(time.Duration(idleAfter)*time.Minute, time.Duration(idleLongBuild)*time.Hour))
conditions.Add("build", condition.NewBuildCondition(
time.Duration(idleAfter)*time.Minute,
time.Duration(idleLongBuild)*time.Hour))

// Add a DeploymentConfig condition.
conditions.Add("DC", condition.NewDCCondition(time.Duration(idleAfter)*time.Minute))
Expand Down
Loading