This repository has been archived by the owner on May 6, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Single user Idler v for test/debug purpose
Input: Namespace Basename, OSIO User UUID, CLuster URL and OS User Token Watches builds and deployments in a single namespace relying only on a User token (require admin/edit of -jenkins ns to perform Idle/Unidle) See cmd/fabric8-jenkins-idler-test/main.go for "var"s required. Run on cli go install -ldflags="-X github.com/fabric8-services/fabric8-jenkins-idler/internal/version.version=c931081" ./cmd/fabric8-jenkins-idler-test && fabric8-jenkins-idler-test Or run/debug the test TestSingleIdler in cmd/fabric8-jenkins-idler-test/main_test.go
- Loading branch information
1 parent
8840ca9
commit b128cb7
Showing
4 changed files
with
411 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/configuration" | ||
|
||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/openshift" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/toggles" | ||
|
||
"context" | ||
"os" | ||
"os/signal" | ||
"sync" | ||
"syscall" | ||
|
||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/api" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/cluster" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/openshift/client" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/router" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/tenant" | ||
"github.com/julienschmidt/httprouter" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
const ( | ||
profilerPort = 6060 | ||
) | ||
|
||
var idlerLogger = log.WithFields(log.Fields{"component": "idler"}) | ||
|
||
// Idler is responsible to create and control the various concurrent processes needed to implement the Jenkins idling | ||
// feature. An Idler instance creates two goroutines for watching all builds respectively deployment config changes of | ||
// the whole cluster. To do this it needs an access openShift access token which allows the Idler to do so (see Data.GetOpenShiftToken). | ||
// A third go routine is used to serve a HTTP REST API. | ||
type Idler struct { | ||
featureService toggles.Features | ||
tenantService tenant.Service | ||
clusterView cluster.View | ||
config configuration.Configuration | ||
baseNamespace string | ||
} | ||
|
||
// NewIdler creates a new instance of Idler. The configuration as well as feature toggle handler needs to be passed. | ||
func NewIdler(features toggles.Features, tenantService tenant.Service, clusterView cluster.View, config configuration.Configuration, baseNamespace string) *Idler { | ||
return &Idler{ | ||
featureService: features, | ||
tenantService: tenantService, | ||
clusterView: clusterView, | ||
config: config, | ||
baseNamespace: baseNamespace, | ||
} | ||
} | ||
|
||
// Run starts the various goroutines of the Idler. To cleanly shutdown the SIGTERM signal should be send to the process. | ||
func (idler *Idler) Run() { | ||
var wg sync.WaitGroup | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
idler.startWorkers(ctx, &wg, cancel, idler.config.GetDebugMode()) | ||
|
||
setupSignalChannel(cancel) | ||
wg.Wait() | ||
idlerLogger.Info("Idler successfully shut down.") | ||
} | ||
|
||
func (idler *Idler) startWorkers(ctx context.Context, wg *sync.WaitGroup, cancel context.CancelFunc, addProfiler bool) { | ||
idlerLogger.Info("Starting all Idler workers") | ||
|
||
// Create synchronized map for UserIdler instances | ||
userIdlers := openshift.NewUserIdlerMap() | ||
|
||
// Start the controllers to monitor the OpenShift clusters | ||
idler.startOpenShiftControllers(ctx, wg, cancel, userIdlers) | ||
|
||
// Start API router | ||
go func() { | ||
// Create and start a Router instance to serve the REST API | ||
idlerAPI := api.NewIdlerAPI(userIdlers, idler.clusterView, idler.tenantService) | ||
apirouter := router.CreateAPIRouter(idlerAPI) | ||
router := router.NewRouter(apirouter) | ||
router.AddMetrics(apirouter) | ||
router.Start(ctx, wg, cancel) | ||
}() | ||
|
||
if addProfiler { | ||
go func() { | ||
idlerLogger.Infof("Starting profiler on port %d", profilerPort) | ||
router := router.NewRouterWithPort(httprouter.New(), profilerPort) | ||
router.Start(ctx, wg, cancel) | ||
}() | ||
} | ||
} | ||
|
||
func (idler *Idler) startOpenShiftControllers(ctx context.Context, wg *sync.WaitGroup, cancel context.CancelFunc, userIdlers *openshift.UserIdlerMap) { | ||
openShiftClient := client.NewOpenShift() | ||
|
||
for _, openShiftCluster := range idler.clusterView.GetClusters() { | ||
// Create Controller | ||
controller := openshift.NewController( | ||
ctx, | ||
openShiftCluster.APIURL, | ||
openShiftCluster.Token, | ||
userIdlers, | ||
idler.tenantService, | ||
idler.featureService, | ||
idler.config, | ||
wg, | ||
cancel, | ||
) | ||
|
||
wg.Add(1) | ||
go func(cluster cluster.Cluster) { | ||
defer wg.Done() | ||
go func() { | ||
idlerLogger.Info("Starting to watch openShift deployment configuration changes.") | ||
var err error | ||
if len(idler.baseNamespace) > 0 { | ||
err = openShiftClient.WatchNamespaceDeploymentConfigs(cluster.APIURL, cluster.Token, idler.baseNamespace+"-jenkins", "-jenkins", controller.HandleDeploymentConfig) | ||
} else { | ||
err = openShiftClient.WatchDeploymentConfigs(cluster.APIURL, cluster.Token, "-jenkins", controller.HandleDeploymentConfig) | ||
} | ||
if err != nil { | ||
cancel() | ||
return | ||
} | ||
}() | ||
|
||
for { | ||
select { | ||
case <-ctx.Done(): | ||
idlerLogger.Infof("Stopping to watch openShift deployment configuration changes.") | ||
cancel() | ||
return | ||
} | ||
} | ||
}(openShiftCluster) | ||
|
||
wg.Add(1) | ||
go func(cluster cluster.Cluster) { | ||
defer wg.Done() | ||
go func() { | ||
idlerLogger.Info("Starting to watch openShift build configuration changes.") | ||
var err error | ||
if len(idler.baseNamespace) > 0 { | ||
err = openShiftClient.WatchNamespaceBuilds(cluster.APIURL, cluster.Token, "JenkinsPipeline", idler.baseNamespace, controller.HandleBuild) | ||
} else { | ||
err = openShiftClient.WatchBuilds(cluster.APIURL, cluster.Token, "JenkinsPipeline", controller.HandleBuild) | ||
} | ||
if err != nil { | ||
cancel() | ||
return | ||
} | ||
}() | ||
|
||
for { | ||
select { | ||
case <-ctx.Done(): | ||
idlerLogger.Infof("Stopping to watch openShift build configuration changes.") | ||
cancel() | ||
return | ||
} | ||
} | ||
}(openShiftCluster) | ||
} | ||
} | ||
|
||
// setupSignalChannel registers a listener for Unix signals for a ordered shutdown | ||
func setupSignalChannel(cancel context.CancelFunc) { | ||
sigChan := make(chan os.Signal, 1) | ||
signal.Notify(sigChan, syscall.SIGTERM) | ||
|
||
go func() { | ||
<-sigChan | ||
idlerLogger.Info("Received SIGTERM signal. Initiating shutdown.") | ||
cancel() | ||
}() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package main | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/cluster" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/configuration" | ||
"github.com/fabric8-services/fabric8-jenkins-idler/internal/tenant" | ||
) | ||
|
||
type testTenantService struct { | ||
IdentityID string | ||
BaseName string | ||
} | ||
|
||
func (t testTenantService) GetTenantInfoByNamespace(apiURL string, ns string) (tenant.InfoList, error) { | ||
if ns == t.BaseName || ns == t.BaseName+"-jenkins" { | ||
return tenant.InfoList{ | ||
Data: []tenant.InfoData{{ | ||
ID: t.IdentityID, | ||
Attributes: tenant.Attributes{ | ||
Namespaces: []tenant.Namespace{ | ||
{ | ||
ClusterURL: apiURL, | ||
Name: ns, | ||
Type: "user", | ||
ClusterCapacityExhausted: false, | ||
}, | ||
{ | ||
ClusterURL: apiURL, | ||
Name: ns + "-jenkins", | ||
Type: "jenkins", | ||
ClusterCapacityExhausted: false, | ||
}, | ||
}, | ||
}, | ||
}}, | ||
}, nil | ||
} | ||
return tenant.InfoList{}, nil | ||
} | ||
|
||
func (t testTenantService) HasReachedMaxCapacity(apiURL, ns string) (bool, error) { | ||
return false, nil | ||
} | ||
|
||
type testFeaturesService struct { | ||
IdentityID string | ||
} | ||
|
||
func (t testFeaturesService) IsIdlerEnabled(uid string) (bool, error) { | ||
if uid == t.IdentityID { | ||
return true, nil | ||
} | ||
return false, nil | ||
} | ||
|
||
func NewTestIdler(baseName, identityID, clusterURL, token string) *Idler { | ||
featuresService := testFeaturesService{ | ||
IdentityID: identityID, | ||
} | ||
tenantService := testTenantService{ | ||
IdentityID: identityID, | ||
BaseName: baseName, | ||
} | ||
clusterView := cluster.NewView([]cluster.Cluster{ | ||
cluster.Cluster{ | ||
APIURL: clusterURL, | ||
AppDNS: "4e1e.starter-us-east-2a.openshiftapps.com", | ||
Token: token, | ||
}, | ||
}) | ||
config, _ := configuration.NewConfiguration() | ||
|
||
return NewIdler(featuresService, tenantService, clusterView, config, baseName) | ||
} | ||
|
||
var ( | ||
baseName = "aslak-preview" | ||
identityID = "81d3c8d8-1aa2-49b4-a4c3-ffb41ed1f439" | ||
clusterURL = "https://api.starter-us-east-2a.openshift.com/" | ||
token = "xxxxxxxx" | ||
) | ||
|
||
func main() { | ||
|
||
os.Setenv("JC_DEBUG_MODE", "true") | ||
os.Setenv("JC_IDLE_AFTER", "5") | ||
os.Setenv("JC_CHECK_INTERVAL", "5") | ||
os.Setenv("JC_MAX_RETRIES", "5") | ||
os.Setenv("JC_MAX_RETRIES_QUIET_INTERVAL", "5") | ||
|
||
idler := NewTestIdler(baseName, identityID, clusterURL, token) | ||
idler.Run() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package main | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
) | ||
|
||
func XTestSingleIdler(t *testing.T) { | ||
|
||
os.Setenv("JC_DEBUG_MODE", "true") | ||
os.Setenv("JC_IDLE_AFTER", "5") | ||
os.Setenv("JC_CHECK_INTERVAL", "5") | ||
os.Setenv("JC_MAX_RETRIES", "5") | ||
os.Setenv("JC_MAX_RETRIES_QUIET_INTERVAL", "5") | ||
|
||
idler := NewTestIdler(baseName, identityID, clusterURL, token) | ||
idler.Run() | ||
} |
Oops, something went wrong.