2019-06-04 20:11:54 +00:00
|
|
|
package app
|
|
|
|
|
|
|
|
import (
|
2020-10-13 20:23:05 +00:00
|
|
|
"net/url"
|
2019-06-09 17:43:48 +00:00
|
|
|
"sync"
|
2019-06-04 20:11:54 +00:00
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
|
2020-06-07 20:04:31 +00:00
|
|
|
"github.com/crazy-max/diun/v4/internal/config"
|
|
|
|
"github.com/crazy-max/diun/v4/internal/db"
|
|
|
|
"github.com/crazy-max/diun/v4/internal/logging"
|
|
|
|
"github.com/crazy-max/diun/v4/internal/model"
|
|
|
|
"github.com/crazy-max/diun/v4/internal/notif"
|
|
|
|
dockerPrd "github.com/crazy-max/diun/v4/internal/provider/docker"
|
|
|
|
filePrd "github.com/crazy-max/diun/v4/internal/provider/file"
|
2020-06-15 17:46:47 +00:00
|
|
|
kubernetesPrd "github.com/crazy-max/diun/v4/internal/provider/kubernetes"
|
2020-06-07 20:04:31 +00:00
|
|
|
swarmPrd "github.com/crazy-max/diun/v4/internal/provider/swarm"
|
|
|
|
"github.com/crazy-max/diun/v4/pkg/registry"
|
2020-10-13 20:23:05 +00:00
|
|
|
"github.com/crazy-max/gohealthchecks"
|
2019-06-28 15:07:20 +00:00
|
|
|
"github.com/hako/durafmt"
|
2019-11-04 20:35:38 +00:00
|
|
|
"github.com/panjf2000/ants/v2"
|
2020-10-13 20:23:05 +00:00
|
|
|
"github.com/pkg/errors"
|
2019-06-30 22:11:11 +00:00
|
|
|
"github.com/robfig/cron/v3"
|
2019-06-04 20:11:54 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Diun represents an active diun object
|
|
|
|
type Diun struct {
|
2020-06-07 19:58:49 +00:00
|
|
|
meta model.Meta
|
|
|
|
cfg *config.Config
|
|
|
|
cron *cron.Cron
|
|
|
|
db *db.Client
|
2020-10-13 20:23:05 +00:00
|
|
|
hc *gohealthchecks.Client
|
2020-06-07 19:58:49 +00:00
|
|
|
notif *notif.Client
|
|
|
|
jobID cron.EntryID
|
|
|
|
locker uint32
|
|
|
|
pool *ants.PoolWithFunc
|
|
|
|
wg *sync.WaitGroup
|
2019-06-04 20:11:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates new diun instance
|
2020-10-31 23:22:39 +00:00
|
|
|
func New(meta model.Meta, cli model.Cli, cfg *config.Config) (*Diun, error) {
|
2020-08-19 22:52:54 +00:00
|
|
|
var err error
|
2019-06-04 20:11:54 +00:00
|
|
|
|
2020-08-19 22:52:54 +00:00
|
|
|
diun := &Diun{
|
2020-06-07 19:58:49 +00:00
|
|
|
meta: meta,
|
|
|
|
cfg: cfg,
|
2020-10-31 23:22:39 +00:00
|
|
|
cron: cron.New(cron.WithParser(cron.NewParser(
|
|
|
|
cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor),
|
2019-12-10 13:57:01 +00:00
|
|
|
)),
|
2020-08-19 22:52:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
diun.notif, err = notif.New(cfg.Notif, meta)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !cli.TestNotif {
|
|
|
|
diun.db, err = db.New(*cfg.Db)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-13 20:23:05 +00:00
|
|
|
if cfg.Watch.Healthchecks != nil {
|
|
|
|
var hcBaseURL *url.URL
|
|
|
|
if len(cfg.Watch.Healthchecks.BaseURL) > 0 {
|
|
|
|
hcBaseURL, err = url.Parse(cfg.Watch.Healthchecks.BaseURL)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "Cannot parse Healthchecks base URL")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
diun.hc = gohealthchecks.NewClient(&gohealthchecks.ClientOptions{
|
|
|
|
BaseURL: hcBaseURL,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-08-19 22:52:54 +00:00
|
|
|
return diun, nil
|
2019-06-04 20:11:54 +00:00
|
|
|
}
|
|
|
|
|
2019-06-30 22:11:11 +00:00
|
|
|
// Start starts diun
|
|
|
|
func (di *Diun) Start() error {
|
|
|
|
var err error
|
|
|
|
|
2020-03-31 21:27:10 +00:00
|
|
|
// Migrate db
|
|
|
|
err = di.db.Migrate()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-06-30 22:11:11 +00:00
|
|
|
// Run on startup
|
2019-06-30 22:15:06 +00:00
|
|
|
di.Run()
|
2019-06-30 22:11:11 +00:00
|
|
|
|
|
|
|
// Init scheduler
|
|
|
|
di.jobID, err = di.cron.AddJob(di.cfg.Watch.Schedule, di)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Info().Msgf("Cron initialized with schedule %s", di.cfg.Watch.Schedule)
|
|
|
|
|
|
|
|
// Start scheduler
|
|
|
|
di.cron.Start()
|
|
|
|
log.Info().Msgf("Next run in %s (%s)",
|
2020-12-26 10:30:46 +00:00
|
|
|
durafmt.ParseShort(time.Until(di.cron.Entry(di.jobID).Next)).String(),
|
2019-06-30 22:11:11 +00:00
|
|
|
di.cron.Entry(di.jobID).Next)
|
|
|
|
|
|
|
|
select {}
|
|
|
|
}
|
|
|
|
|
2019-12-13 22:04:02 +00:00
|
|
|
// Run starts diun
|
2019-06-04 20:11:54 +00:00
|
|
|
func (di *Diun) Run() {
|
|
|
|
if !atomic.CompareAndSwapUint32(&di.locker, 0, 1) {
|
|
|
|
log.Warn().Msg("Already running")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer atomic.StoreUint32(&di.locker, 0)
|
2019-06-30 22:56:23 +00:00
|
|
|
if di.jobID > 0 {
|
|
|
|
defer log.Info().Msgf("Next run in %s (%s)",
|
2020-12-26 10:30:46 +00:00
|
|
|
durafmt.ParseShort(time.Until(di.cron.Entry(di.jobID).Next)).String(),
|
2019-06-30 22:56:23 +00:00
|
|
|
di.cron.Entry(di.jobID).Next)
|
|
|
|
}
|
2019-06-04 20:11:54 +00:00
|
|
|
|
2019-12-14 15:41:01 +00:00
|
|
|
log.Info().Msg("Cron triggered")
|
2020-10-13 20:23:05 +00:00
|
|
|
entries := new(model.NotifEntries)
|
|
|
|
di.HealthchecksStart()
|
|
|
|
defer di.HealthchecksSuccess(entries)
|
|
|
|
|
2019-06-28 15:07:20 +00:00
|
|
|
di.wg = new(sync.WaitGroup)
|
|
|
|
di.pool, _ = ants.NewPoolWithFunc(di.cfg.Watch.Workers, func(i interface{}) {
|
2019-12-13 22:04:02 +00:00
|
|
|
job := i.(model.Job)
|
2020-10-13 20:23:05 +00:00
|
|
|
entries.Add(di.runJob(job))
|
2019-06-28 15:07:20 +00:00
|
|
|
di.wg.Done()
|
2020-05-27 21:08:39 +00:00
|
|
|
}, ants.WithLogger(new(logging.AntsLogger)))
|
2019-11-04 20:35:38 +00:00
|
|
|
defer di.pool.Release()
|
2019-06-04 20:11:54 +00:00
|
|
|
|
2019-12-13 22:04:02 +00:00
|
|
|
// Docker provider
|
|
|
|
for _, job := range dockerPrd.New(di.cfg.Providers.Docker).ListJob() {
|
|
|
|
di.createJob(job)
|
|
|
|
}
|
|
|
|
|
2019-12-14 02:55:58 +00:00
|
|
|
// Swarm provider
|
|
|
|
for _, job := range swarmPrd.New(di.cfg.Providers.Swarm).ListJob() {
|
|
|
|
di.createJob(job)
|
|
|
|
}
|
|
|
|
|
2020-06-15 17:46:47 +00:00
|
|
|
// Kubernetes provider
|
|
|
|
for _, job := range kubernetesPrd.New(di.cfg.Providers.Kubernetes).ListJob() {
|
|
|
|
di.createJob(job)
|
|
|
|
}
|
|
|
|
|
2020-05-25 12:08:12 +00:00
|
|
|
// File provider
|
|
|
|
for _, job := range filePrd.New(di.cfg.Providers.File).ListJob() {
|
2019-12-13 22:04:02 +00:00
|
|
|
di.createJob(job)
|
|
|
|
}
|
|
|
|
|
2019-06-28 15:07:20 +00:00
|
|
|
di.wg.Wait()
|
2020-10-13 20:23:05 +00:00
|
|
|
log.Info().
|
|
|
|
Int("added", entries.CountNew).
|
|
|
|
Int("updated", entries.CountUpdate).
|
|
|
|
Int("unchanged", entries.CountUnchange).
|
|
|
|
Int("failed", entries.CountError).
|
|
|
|
Msg("Jobs completed")
|
2019-06-04 20:11:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes diun
|
|
|
|
func (di *Diun) Close() {
|
2020-10-13 20:23:05 +00:00
|
|
|
di.HealthchecksFail("Application closed")
|
2019-06-30 22:11:11 +00:00
|
|
|
if di.cron != nil {
|
|
|
|
di.cron.Stop()
|
|
|
|
}
|
2019-06-04 20:11:54 +00:00
|
|
|
if err := di.db.Close(); err != nil {
|
|
|
|
log.Warn().Err(err).Msg("Cannot close database")
|
|
|
|
}
|
|
|
|
}
|
2020-05-26 02:55:23 +00:00
|
|
|
|
|
|
|
// TestNotif test the notification settings
|
|
|
|
func (di *Diun) TestNotif() {
|
|
|
|
createdAt, _ := time.Parse("2006-01-02T15:04:05Z", "2020-03-26T12:23:56Z")
|
2020-06-08 19:10:18 +00:00
|
|
|
image, _ := registry.ParseImage(registry.ParseImageOptions{
|
2020-08-19 22:52:54 +00:00
|
|
|
Name: "diun/testnotif:latest",
|
2020-06-08 19:10:18 +00:00
|
|
|
})
|
2020-08-19 22:52:54 +00:00
|
|
|
image.HubLink = ""
|
2020-05-26 02:55:23 +00:00
|
|
|
|
|
|
|
log.Info().Msg("Testing notification settings...")
|
|
|
|
di.notif.Send(model.NotifEntry{
|
|
|
|
Status: "new",
|
|
|
|
Provider: "file",
|
|
|
|
Image: image,
|
|
|
|
Manifest: registry.Manifest{
|
2020-08-19 22:52:54 +00:00
|
|
|
Name: "diun/testnotif",
|
2020-05-26 02:55:23 +00:00
|
|
|
Tag: "latest",
|
|
|
|
MIMEType: "application/vnd.docker.distribution.manifest.list.v2+json",
|
|
|
|
Digest: "sha256:216e3ae7de4ca8b553eb11ef7abda00651e79e537e85c46108284e5e91673e01",
|
|
|
|
Created: &createdAt,
|
|
|
|
DockerVersion: "",
|
|
|
|
Labels: map[string]string{
|
|
|
|
"maintainer": "CrazyMax",
|
|
|
|
"org.label-schema.build-date": "2020-03-26T12:23:56Z",
|
|
|
|
"org.label-schema.description": "Docker image update notifier",
|
|
|
|
"org.label-schema.name": "Diun",
|
|
|
|
"org.label-schema.schema-version": "1.0",
|
|
|
|
"org.label-schema.url": "https://github.com/crazy-max/diun",
|
|
|
|
"org.label-schema.vcs-ref": "e13f097c",
|
|
|
|
"org.label-schema.vcs-url": "https://github.com/crazy-max/diun",
|
|
|
|
"org.label-schema.vendor": "CrazyMax",
|
2020-08-19 22:52:54 +00:00
|
|
|
"org.label-schema.version": "x.x.x",
|
2020-05-26 02:55:23 +00:00
|
|
|
},
|
|
|
|
Layers: []string{
|
|
|
|
"sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
|
|
|
|
"sha256:166c6f165b73185ede72415d780538a55c0c8e854bd177925bc007193e5b0d1b",
|
|
|
|
"sha256:e05682efa9cc9d6239b2b9252fe0dc1e58d6e1585679733bb94a6549d49e9b10",
|
|
|
|
"sha256:c6a5bfed445b3ed7e85523cd73c6532ac9f9b72bb588ca728fd5b33987ca6538",
|
|
|
|
"sha256:df2140efb8abeb727ef0b27ff158b7010a7941eb1cfdade505f510a6e1eaf016",
|
|
|
|
},
|
2020-06-08 23:19:30 +00:00
|
|
|
Platform: "linux/amd64",
|
2020-05-26 02:55:23 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|