Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
G
gitlab-runner-do-monitor
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Server Tools
gitlab-runner-do-monitor
Merge requests
!1
Initial release
Code
Review changes
Check out branch
Download
Patches
Plain diff
Merged
Initial release
fix/1-initial-release
into
master
Overview
0
Commits
14
Pipelines
8
Changes
1
Merged
Erick Hitter
requested to merge
fix/1-initial-release
into
master
6 years ago
Overview
0
Commits
14
Pipelines
8
Changes
1
Expand
Fixes
#1 (closed)
Edited
6 years ago
by
Erick Hitter
0
0
Merge request reports
Viewing commit
58a1bbae
Prev
Next
Show latest version
1 file
+
11
−
9
Inline
Compare changes
Side-by-side
Inline
Show whitespace changes
Show one file at a time
58a1bbae
Linting
· 58a1bbae
Erick Hitter
authored
6 years ago
glrdomon.go
+
234
−
0
Options
package
main
import
(
"context"
"encoding/json"
"flag"
"io/ioutil"
"log"
"os"
"path/filepath"
"sync"
"time"
"github.com/digitalocean/godo"
"github.com/dustin/go-humanize"
"golang.org/x/oauth2"
)
type
config
struct
{
LogDest
string
`json:"log-dest"`
APIKey
string
`json:"api-key"`
Threshold
int
`json:"threshold"`
DeleteStale
bool
`json:"delete-stale"`
}
type
tokenSource
struct
{
AccessToken
string
}
var
(
configPath
string
logger
*
log
.
Logger
logDest
string
apiKey
string
threshold
int
deleteStale
bool
wg
sync
.
WaitGroup
client
*
godo
.
Client
)
func
init
()
{
flag
.
StringVar
(
&
configPath
,
"config"
,
"./config.json"
,
"Path to configuration file"
)
flag
.
Parse
()
if
!
validatePath
(
&
configPath
)
{
usage
()
}
configFile
,
err
:=
ioutil
.
ReadFile
(
configPath
)
if
err
!=
nil
{
usage
()
}
cfg
:=
config
{}
if
err
=
json
.
Unmarshal
(
configFile
,
&
cfg
);
err
!=
nil
{
usage
()
}
apiKey
=
cfg
.
APIKey
threshold
=
cfg
.
Threshold
deleteStale
=
cfg
.
DeleteStale
logDest
=
cfg
.
LogDest
setUpLogger
()
logger
.
Printf
(
"Starting GitLab Runner monitoring with config %s"
,
configPath
)
if
deleteStale
{
logger
.
Println
(
"Stale droplets WILL BE DELETED automatically"
)
}
else
{
logger
.
Println
(
"Stale droplets will be logged, but not deleted"
)
}
}
func
main
()
{
authenticate
()
checkAPI
()
wg
.
Wait
()
logger
.
Println
(
"Execution complete!"
)
}
func
authenticate
()
{
tokenSource
:=
&
tokenSource
{
AccessToken
:
apiKey
,
}
oauthClient
:=
oauth2
.
NewClient
(
context
.
Background
(),
tokenSource
)
client
=
godo
.
NewClient
(
oauthClient
)
}
// oAuth token
func
(
t
*
tokenSource
)
Token
()
(
*
oauth2
.
Token
,
error
)
{
token
:=
&
oauth2
.
Token
{
AccessToken
:
t
.
AccessToken
,
}
return
token
,
nil
}
func
checkAPI
()
{
ctx
:=
context
.
TODO
()
droplets
,
err
:=
listDroplets
(
ctx
,
client
)
if
err
!=
nil
{
logger
.
Println
(
"Warning! Failed to retrieve droplet list."
)
logger
.
Print
(
err
)
return
}
for
_
,
droplet
:=
range
droplets
{
wg
.
Add
(
1
)
go
checkDroplet
(
droplet
)
}
}
func
listDroplets
(
ctx
context
.
Context
,
client
*
godo
.
Client
)
([]
godo
.
Droplet
,
error
)
{
list
:=
[]
godo
.
Droplet
{}
opt
:=
&
godo
.
ListOptions
{}
for
{
droplets
,
resp
,
err
:=
client
.
Droplets
.
List
(
ctx
,
opt
)
if
err
!=
nil
{
return
nil
,
err
}
for
_
,
d
:=
range
droplets
{
list
=
append
(
list
,
d
)
}
if
resp
.
Links
==
nil
||
resp
.
Links
.
IsLastPage
()
{
break
}
page
,
err
:=
resp
.
Links
.
CurrentPage
()
if
err
!=
nil
{
return
nil
,
err
}
opt
.
Page
=
page
+
1
}
return
list
,
nil
}
func
checkDroplet
(
droplet
godo
.
Droplet
)
{
defer
wg
.
Done
()
if
!
checkDropletAge
(
droplet
)
{
return
}
deleted
:=
deleteDroplet
(
droplet
)
if
deleteStale
{
if
deleted
{
logger
.
Printf
(
"Removed droplet %s (%d)"
,
droplet
.
Name
,
droplet
.
ID
)
}
else
{
logger
.
Printf
(
"Failed to delete droplet %s (%d)"
,
droplet
.
Name
,
droplet
.
ID
)
}
}
}
func
checkDropletAge
(
droplet
godo
.
Droplet
)
bool
{
thr
:=
time
.
Now
()
.
Add
(
time
.
Duration
(
-
threshold
)
*
time
.
Second
)
created
,
err
:=
time
.
Parse
(
time
.
RFC3339
,
droplet
.
Created
)
if
err
!=
nil
{
logger
.
Printf
(
"Could not parse created-timestamp for droplet ID %d"
,
droplet
.
ID
)
return
false
}
stale
:=
thr
.
After
(
created
)
if
stale
{
logger
.
Printf
(
"Stale droplet => ID: %d; name:
\"
%s
\"
; created: %s, %s (%d)"
,
droplet
.
ID
,
droplet
.
Name
,
humanize
.
Time
(
created
),
droplet
.
Created
,
created
.
Unix
())
}
return
stale
}
func
deleteDroplet
(
droplet
godo
.
Droplet
)
bool
{
if
!
deleteStale
{
return
false
}
logger
.
Printf
(
"Deleting droplet %s (%d)"
,
droplet
.
Name
,
droplet
.
ID
)
ctx
:=
context
.
TODO
()
_
,
err
:=
client
.
Droplets
.
Delete
(
ctx
,
droplet
.
ID
)
return
err
==
nil
}
func
setUpLogger
()
{
logOpts
:=
log
.
Ldate
|
log
.
Ltime
|
log
.
LUTC
|
log
.
Lshortfile
if
logDest
==
"os.Stdout"
{
logger
=
log
.
New
(
os
.
Stdout
,
"DEBUG: "
,
logOpts
)
}
else
{
path
,
err
:=
filepath
.
Abs
(
logDest
)
if
err
!=
nil
{
logger
.
Fatal
(
err
)
}
logFile
,
err
:=
os
.
OpenFile
(
path
,
os
.
O_WRONLY
|
os
.
O_CREATE
|
os
.
O_APPEND
,
0644
)
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
logger
=
log
.
New
(
logFile
,
""
,
logOpts
)
}
}
func
validatePath
(
path
*
string
)
bool
{
if
len
(
*
path
)
<=
1
{
return
false
}
var
err
error
*
path
,
err
=
filepath
.
Abs
(
*
path
)
if
err
!=
nil
{
logger
.
Printf
(
"Error: %s"
,
err
.
Error
())
return
false
}
if
_
,
err
=
os
.
Stat
(
*
path
);
os
.
IsNotExist
(
err
)
{
return
false
}
return
true
}
func
usage
()
{
flag
.
Usage
()
os
.
Exit
(
3
)
}
Loading