diff --git a/main.go b/main.go index c0b32842..b9c389c9 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ func main() { _ = flag.Bool("status-updater", false, "start status updater") _ = flag.Bool("job-executor", false, "start job executor") _ = flag.Bool("repairer", false, "start repairer") + _ = flag.Bool("pinger", false, "start pinger") flag.Parse() @@ -31,15 +32,17 @@ func main() { statusUpdater := isFlagPassed("status-updater") jobExecutor := isFlagPassed("job-executor") repairer := isFlagPassed("repairer") + pinger := isFlagPassed("pinger") var options *app.StartOptions - if confirmer || statusUpdater || jobExecutor || repairer || api { + if confirmer || statusUpdater || jobExecutor || repairer || api || pinger { options = &app.StartOptions{ API: api, Confirmer: confirmer, StatusUpdater: statusUpdater, JobExecutor: jobExecutor, Repairer: repairer, + Pinger: pinger, } log.Println("api: ", options.API) @@ -47,6 +50,7 @@ func main() { log.Println("status-updater: ", options.StatusUpdater) log.Println("job-executor: ", options.JobExecutor) log.Println("repairer: ", options.Repairer) + log.Println("pinger: ", options.Pinger) } else { log.Println("no workers specified, starting all") } diff --git a/models/dto/body/deployment.go b/models/dto/body/deployment.go index 2641642c..6b5c09c2 100644 --- a/models/dto/body/deployment.go +++ b/models/dto/body/deployment.go @@ -51,6 +51,7 @@ type DeploymentRead struct { Envs []Env `json:"envs"` Private bool `json:"private"` Integrations []string `json:"integrations"` + PingResult *int `json:"pingResult,omitempty"` } type CiConfig struct { diff --git a/models/sys/deployment/converters.go b/models/sys/deployment/converters.go index 2d9d4d06..5d00e41b 100644 --- a/models/sys/deployment/converters.go +++ b/models/sys/deployment/converters.go @@ -29,6 +29,11 @@ func (deployment *Deployment) ToDTO(url *string) body.DeploymentRead { integrations = append(integrations, "github") } + var pingResult *int + if deployment.PingResult != 0 { + pingResult = &deployment.PingResult + } + return body.DeploymentRead{ ID: deployment.ID, Name: deployment.Name, @@ -38,6 +43,7 @@ func (deployment *Deployment) ToDTO(url *string) body.DeploymentRead { Envs: envs, Private: deployment.Private, Integrations: integrations, + PingResult: pingResult, } } diff --git a/models/sys/deployment/deployment.go b/models/sys/deployment/deployment.go index d2becf94..685501c1 100644 --- a/models/sys/deployment/deployment.go +++ b/models/sys/deployment/deployment.go @@ -21,6 +21,8 @@ type Deployment struct { Subsystems Subsystems `bson:"subsystems"` StatusCode int `bson:"statusCode"` StatusMessage string `bson:"statusMessage"` + + PingResult int `bson:"pingResult"` } func (deployment *Deployment) Ready() bool { diff --git a/models/sys/deployment/helpers.go b/models/sys/deployment/helpers.go index 49d1c09a..5408c48d 100644 --- a/models/sys/deployment/helpers.go +++ b/models/sys/deployment/helpers.go @@ -363,3 +363,24 @@ func GetLastGitLabBuild(deploymentID string) (*GitLabBuild, error) { return &deployment.Subsystems.GitLab.LastBuild, nil } + +func SavePing(id string, pingResult int) error { + updateData := bson.M{} + + models.AddIfNotNil(updateData, "pingResult", pingResult) + + if len(updateData) == 0 { + return nil + } + + _, err := models.DeploymentCollection.UpdateOne(context.TODO(), + bson.D{{"id", id}}, + bson.D{{"$set", updateData}}, + ) + if err != nil { + return fmt.Errorf("failed to update deployment ping result %s. details: %s", id, err) + } + + return nil + +} diff --git a/pkg/app/server.go b/pkg/app/server.go index 0cd70a44..f155f5de 100644 --- a/pkg/app/server.go +++ b/pkg/app/server.go @@ -11,6 +11,7 @@ import ( "go-deploy/pkg/sys" "go-deploy/pkg/workers/confirm" "go-deploy/pkg/workers/job_execute" + "go-deploy/pkg/workers/ping" "go-deploy/pkg/workers/migrator" "go-deploy/pkg/workers/repair" "go-deploy/pkg/workers/status_update" @@ -27,6 +28,7 @@ type StartOptions struct { StatusUpdater bool JobExecutor bool Repairer bool + Pinger bool } func shutdown() { @@ -56,6 +58,7 @@ func Start(options *StartOptions) *http.Server { StatusUpdater: true, JobExecutor: true, Repairer: true, + Pinger: true, } } @@ -71,6 +74,9 @@ func Start(options *StartOptions) *http.Server { if options.Repairer { repair.Setup(c) } + if options.Pinger { + ping.Setup(c) + } if options.API { ginMode, exists := os.LookupEnv("GIN_MODE") if exists { diff --git a/pkg/conf/environment.go b/pkg/conf/environment.go index 7bec259a..0b1f03ca 100644 --- a/pkg/conf/environment.go +++ b/pkg/conf/environment.go @@ -41,6 +41,7 @@ type Environment struct { ExtraDomainIP string `yaml:"extraDomainIp"` IngressClass string `yaml:"ingressClass"` RepairInterval int `yaml:"repairInterval"` + PingInterval int `yaml:"pingInterval"` Resources struct { Limits struct { CPU string `yaml:"cpu"` diff --git a/pkg/workers/ping/ping.go b/pkg/workers/ping/ping.go new file mode 100644 index 00000000..f7ea18c8 --- /dev/null +++ b/pkg/workers/ping/ping.go @@ -0,0 +1,11 @@ +package ping + +import ( + "go-deploy/pkg/sys" + "log" +) + +func Setup(ctx *sys.Context) { + log.Println("starting status updaters") + go deploymentPingUpdater(ctx) +} diff --git a/pkg/workers/ping/workers.go b/pkg/workers/ping/workers.go new file mode 100644 index 00000000..02a8b9c1 --- /dev/null +++ b/pkg/workers/ping/workers.go @@ -0,0 +1,67 @@ +package ping + +import ( + deploymentModels "go-deploy/models/sys/deployment" + "go-deploy/pkg/conf" + "go-deploy/pkg/sys" + "go-deploy/service/deployment_service" + "log" + "net/http" + "time" +) + +func deploymentPingUpdater(ctx *sys.Context) { + log.Println("starting deployment ping updater") + for { + if ctx.Stop { + break + } + + updateAllDeploymentPings() + time.Sleep(time.Duration(conf.Env.Deployment.PingInterval) * time.Second) + } +} + +func updateAllDeploymentPings() { + deployments, _ := deployment_service.GetAll() + + log.Println("pinging", len(deployments), "deployments") + + for _, deployment := range deployments { + + url := getURL(&deployment) + + if url == nil { + log.Println("deployment has no url") + continue + } + + go updateOneDeploymentPing(&deployment, *url) + } +} + +func updateOneDeploymentPing(deployment *deploymentModels.Deployment, url string) { + code, err := ping(url) + + if err != nil { + log.Println("error fetching deployment status ping. details:", err) + } + + _ = deployment_service.SavePing(deployment.ID, code) +} + +func ping(url string) (int, error) { + resp, err := http.Get("https://" + url) + if err != nil { + return 0, err + } + + return resp.StatusCode, nil +} + +func getURL(deployment *deploymentModels.Deployment) *string { + if len(deployment.Subsystems.K8s.Ingress.Hosts) > 0 && len(deployment.Subsystems.K8s.Ingress.Hosts[0]) > 0 { + return &deployment.Subsystems.K8s.Ingress.Hosts[0] + } + return nil +} diff --git a/service/deployment_service/deployment_service.go b/service/deployment_service/deployment_service.go index 50a97d40..05052343 100644 --- a/service/deployment_service/deployment_service.go +++ b/service/deployment_service/deployment_service.go @@ -455,3 +455,26 @@ func build(deployment *deploymentModel.Deployment, params *deploymentModel.Build return nil } + +func SavePing(id string, pingResult int) error { + makeError := func(err error) error { + return fmt.Errorf("failed to update deployment with ping result. details: %s", err) + } + + deployment, err := deploymentModel.GetByID(id) + if err != nil { + return makeError(err) + } + + if deployment == nil { + log.Println("deployment", id, "not found when updating ping result. assuming it was deleted") + return nil + } + + err = deploymentModel.SavePing(id, pingResult) + if err != nil { + return makeError(err) + } + + return nil +}