2022-09-04 09:38:10 +00:00
package main
import (
2022-09-05 10:20:33 +00:00
"crypto/rand"
"encoding/base64"
2022-09-05 01:29:47 +00:00
"flag"
2022-09-04 09:38:10 +00:00
"fmt"
"log"
"net/http"
2022-11-30 04:15:18 +00:00
"net/url"
2023-01-01 20:11:20 +00:00
"path/filepath"
2022-09-04 09:38:10 +00:00
"strconv"
2022-09-05 01:30:10 +00:00
"strings"
2022-09-04 09:38:10 +00:00
"time"
2023-01-01 20:11:20 +00:00
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
2022-09-05 01:29:47 +00:00
"github.com/drone/drone-go/drone"
2022-09-04 09:38:10 +00:00
"github.com/gin-gonic/gin"
2022-09-05 01:29:47 +00:00
"golang.org/x/oauth2"
2022-09-04 09:38:10 +00:00
)
2022-09-05 01:29:47 +00:00
var (
2023-03-05 02:57:58 +00:00
droneToken = ""
droneConfig * http . Client
droneEndpoint string
testsCallbackToken string
2022-09-05 01:29:47 +00:00
)
func init ( ) {
flag . StringVar ( & droneToken , "drone-token" , droneToken , "Token for Drone Oauth" )
flag . StringVar ( & droneEndpoint , "drone-endpoint" , droneEndpoint , "Drone Endpoint" )
2023-03-05 02:57:58 +00:00
flag . StringVar ( & testsCallbackToken , "tests-callback-token" , testsCallbackToken , "Token of the callback token" )
2022-09-05 01:29:47 +00:00
}
func initializeDroneOauth ( ) {
if droneToken != "" {
config := new ( oauth2 . Config )
droneConfig = config . Client (
oauth2 . NoContext ,
& oauth2 . Token {
AccessToken : droneToken ,
} ,
)
}
}
2022-11-11 12:27:09 +00:00
type RepositoryAdminPull struct {
Tag * string ` json:"tag" `
OptionalSignature bool ` json:"sig_optional" `
}
2022-09-04 09:38:10 +00:00
func declareAPIAuthRepositoriesRoutes ( router * gin . RouterGroup ) {
router . GET ( "/repositories" , func ( c * gin . Context ) {
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
u = c . MustGet ( "LoggedUser" ) . ( * User )
}
repositories , err := u . GetRepositories ( )
if err != nil {
log . Println ( "Unable to GetRepositories:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to retrieve your repositories. Please try again in a few moment." } )
return
}
2022-09-16 15:26:02 +00:00
if work , ok := c . Get ( "work" ) ; ok {
var res [ ] * Repository
for _ , r := range repositories {
if r . IdWork == work . ( * Work ) . Id {
2022-09-30 10:41:34 +00:00
// Is the URL used elsewhere?
repos , _ := getRepositoriesByURI ( r . URI )
if len ( repos ) > 1 {
r . AlreadyUsed = true
}
2022-09-16 15:26:02 +00:00
res = append ( res , r )
}
}
c . JSON ( http . StatusOK , res )
} else {
c . JSON ( http . StatusOK , repositories )
}
2022-09-04 09:38:10 +00:00
} )
router . POST ( "/repositories" , func ( c * gin . Context ) {
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
u = c . MustGet ( "LoggedUser" ) . ( * User )
}
var repository Repository
if err := c . ShouldBindJSON ( & repository ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
}
2022-11-30 04:15:18 +00:00
uri , err := url . Parse ( repository . URI )
if err != nil {
tmp := strings . Split ( repository . URI , ":" )
if len ( tmp ) == 2 {
uri , err = url . Parse ( fmt . Sprintf ( "ssh://%s/%s" , tmp [ 0 ] , tmp [ 1 ] ) )
} else if len ( tmp ) == 3 {
uri , err = url . Parse ( fmt . Sprintf ( "%s://%s/%s" , tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] ) )
}
if err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : fmt . Sprintf ( "invalid repository URL: %s" , err . Error ( ) ) } )
return
}
}
if strings . Contains ( uri . Host , "epita.fr" ) {
if ! strings . HasPrefix ( uri . Path , fmt . Sprintf ( "/%s/" , u . Login ) ) {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : "repository URL forbidden" } )
return
}
}
2022-09-04 15:59:36 +00:00
var w * Work
if work , ok := c . Get ( "work" ) ; ok {
w = work . ( * Work )
} else if repository . IdWork > 0 {
var err error
2023-03-05 02:57:37 +00:00
w , err = getWork ( repository . IdWork )
2022-09-04 15:59:36 +00:00
if err != nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Unable to find the given work identifier." } )
return
}
} else {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : "Unable to find the given work identifier." } )
return
}
2022-09-04 09:38:10 +00:00
k , err := u . NewRepository ( w , repository . URI )
if err != nil {
log . Println ( "Unable to NewRepository:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to register your public repository. Please try again in a few moment." } )
return
}
c . JSON ( http . StatusOK , k )
} )
repositoriesRoutes := router . Group ( "/repositories/:rid" )
repositoriesRoutes . Use ( repositoryHandler )
repositoriesRoutes . GET ( "" , func ( c * gin . Context ) {
repo := c . MustGet ( "repository" ) . ( * Repository )
2022-09-30 10:41:34 +00:00
// Is the URL used elsewhere?
repos , _ := getRepositoriesByURI ( repo . URI )
if len ( repos ) > 1 {
repo . AlreadyUsed = true
}
2022-09-04 09:38:10 +00:00
c . JSON ( http . StatusOK , repo )
} )
repositoriesRoutes . PUT ( "" , func ( c * gin . Context ) {
current := c . MustGet ( "repository" ) . ( * Repository )
var new Repository
if err := c . ShouldBindJSON ( & new ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
}
new . Id = current . Id
u := c . MustGet ( "LoggedUser" ) . ( * User )
if new . IdUser != current . IdUser && ! u . IsAdmin {
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Operation not allowed." } )
return
}
if repository , err := new . Update ( ) ; err != nil {
log . Println ( "Unable to Update repository:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : fmt . Sprintf ( "An error occurs during repository updation: %s" , err . Error ( ) ) } )
return
} else {
c . JSON ( http . StatusOK , repository )
}
} )
repositoriesRoutes . DELETE ( "" , func ( c * gin . Context ) {
2022-11-20 14:53:50 +00:00
loggeduser := c . MustGet ( "LoggedUser" ) . ( * User )
2022-09-04 09:38:10 +00:00
repository := c . MustGet ( "repository" ) . ( * Repository )
2023-03-05 02:57:37 +00:00
work , err := getWork ( repository . IdWork )
2022-09-05 03:13:30 +00:00
if err != nil {
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to find related work." } )
return
}
now := time . Now ( )
2022-11-20 14:53:50 +00:00
if ! loggeduser . IsAdmin && ( ! work . Shown || work . Corrected || work . StartAvailability . After ( now ) || work . EndAvailability . Before ( now ) ) {
2022-09-05 03:13:30 +00:00
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "The submission is closed." } )
return
}
2022-09-04 09:38:10 +00:00
if _ , err := repository . Delete ( ) ; err != nil {
log . Println ( "Unable to Delete repository:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : fmt . Sprintf ( "An error occurs during repository deletion: %s" , err . Error ( ) ) } )
return
} else {
c . JSON ( http . StatusOK , nil )
}
} )
2022-09-04 21:54:10 +00:00
repositoriesRoutes . POST ( "/trigger" , func ( c * gin . Context ) {
2022-09-25 15:41:09 +00:00
loggeduser := c . MustGet ( "LoggedUser" ) . ( * User )
2022-09-05 01:29:47 +00:00
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
2022-09-25 15:41:09 +00:00
u = loggeduser
2022-09-05 01:29:47 +00:00
}
2022-09-04 21:54:10 +00:00
repo := c . MustGet ( "repository" ) . ( * Repository )
2023-03-05 02:57:37 +00:00
work , err := getWork ( repo . IdWork )
2022-09-05 01:29:47 +00:00
if err != nil {
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to find related work." } )
return
}
2022-09-04 21:54:10 +00:00
now := time . Now ( )
2022-09-25 15:41:09 +00:00
if repo . LastCheck != nil && ! repo . LastCheck . Before ( now . Add ( - 5 * time . Minute ) ) && ! loggeduser . IsAdmin {
2022-09-04 21:54:10 +00:00
c . AbortWithStatusJSON ( http . StatusPaymentRequired , gin . H { "errmsg" : "Please wait between two pulls." } )
return
2022-09-05 02:40:49 +00:00
}
2022-09-04 21:54:10 +00:00
2022-11-11 12:27:09 +00:00
var rap RepositoryAdminPull
2022-09-25 15:41:47 +00:00
if loggeduser . IsAdmin {
2022-11-11 12:27:09 +00:00
c . ShouldBindJSON ( & rap )
2022-09-25 15:41:47 +00:00
}
2022-11-11 12:27:09 +00:00
TriggerTagUpdate ( c , work , repo , u , rap . Tag , rap . OptionalSignature )
2022-09-04 21:54:10 +00:00
} )
2022-09-05 01:30:10 +00:00
repositoriesRoutes . GET ( "/state" , func ( c * gin . Context ) {
repo := c . MustGet ( "repository" ) . ( * Repository )
tmp := strings . Split ( repo . DroneRef , "/" )
2022-09-05 02:40:49 +00:00
if len ( tmp ) < 3 {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "No build number. Please try pulling your work." } )
return
}
2022-09-05 01:30:10 +00:00
nbuild , err := strconv . Atoi ( tmp [ 2 ] )
if err != nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Bad build number. Please retry pulling your work." } )
return
}
client := drone . NewClient ( droneEndpoint , droneConfig )
result , err := client . Build ( tmp [ 0 ] , tmp [ 1 ] , nbuild )
if err != nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Unable to find the referenced extraction." } )
return
}
c . JSON ( http . StatusOK , result )
2022-09-05 02:07:41 +00:00
} )
2022-09-05 02:58:48 +00:00
repositoriesRoutes . GET ( "/state-logs" , func ( c * gin . Context ) {
repo := c . MustGet ( "repository" ) . ( * Repository )
tmp := strings . Split ( repo . DroneRef , "/" )
if len ( tmp ) < 3 {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "No build number. Please try pulling your work." } )
return
}
nbuild , err := strconv . Atoi ( tmp [ 2 ] )
if err != nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Bad build number. Please retry pulling your work." } )
return
}
client := drone . NewClient ( droneEndpoint , droneConfig )
2022-09-13 20:36:29 +00:00
result , err := client . Logs ( tmp [ 0 ] , tmp [ 1 ] , nbuild , 1 , 3 )
2022-09-05 02:58:48 +00:00
if err != nil {
2022-09-16 10:03:50 +00:00
log . Printf ( "An error occurs when retrieving logs from Drone (%s/%s/%d/1/3): %s" , tmp [ 0 ] , tmp [ 1 ] , nbuild , err . Error ( ) )
2022-09-05 02:58:48 +00:00
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Unable to retrieve logs." } )
return
}
if len ( result ) > 7 {
c . JSON ( http . StatusOK , result [ 7 : ] )
} else {
c . JSON ( http . StatusOK , result )
}
} )
2023-01-01 20:11:20 +00:00
repositoriesRoutes . POST ( "/gradation" , func ( c * gin . Context ) {
loggeduser := c . MustGet ( "LoggedUser" ) . ( * User )
if ! loggeduser . IsAdmin {
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Permission denied." } )
return
}
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
u = loggeduser
}
repo := c . MustGet ( "repository" ) . ( * Repository )
2023-03-05 02:57:37 +00:00
work , err := getWork ( repo . IdWork )
2023-01-01 20:11:20 +00:00
if err != nil {
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to find related work." } )
return
}
TriggerTests ( c , work , repo , u )
} )
2023-03-05 13:24:06 +00:00
2023-03-06 02:44:39 +00:00
repositoriesRoutes . GET ( "/gradation_status" , func ( c * gin . Context ) {
loggeduser := c . MustGet ( "LoggedUser" ) . ( * User )
if ! loggeduser . IsAdmin {
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Permission denied." } )
return
}
repo := c . MustGet ( "repository" ) . ( * Repository )
slug := strings . Split ( repo . TestsRef , "/" )
if len ( slug ) < 3 {
return
}
buildn , err := strconv . ParseInt ( slug [ 2 ] , 10 , 32 )
if err != nil {
return
}
client := drone . NewClient ( droneEndpoint , droneConfig )
build , err := client . Build ( slug [ 0 ] , slug [ 1 ] , int ( buildn ) )
if err != nil {
log . Println ( "Unable to communicate with Drone:" , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to communicate with Drone" } )
return
}
c . JSON ( http . StatusOK , build )
} )
2023-03-05 13:24:06 +00:00
repositoriesRoutes . GET ( "/traces" , func ( c * gin . Context ) {
repo := c . MustGet ( "repository" ) . ( * Repository )
c . Redirect ( http . StatusFound , fmt . Sprintf ( "%s/%s" , droneEndpoint , repo . TestsRef ) )
} )
2022-09-04 09:38:10 +00:00
}
2022-09-05 10:20:33 +00:00
type GitLabWebhook struct {
EventName string ` json:"event_name" `
ObjectKind string ` json:"object_kind" `
Ref string
Repository GitLabRepository
}
2022-09-05 09:00:46 +00:00
func declareCallbacksRoutes ( router * gin . RouterGroup ) {
router . POST ( "/callbacks/trigger.json" , func ( c * gin . Context ) {
2022-09-05 10:20:33 +00:00
// Check event type
if c . Request . Header . Get ( "X-Gitlab-Event" ) != "Tag Push Hook" {
2022-09-30 10:40:48 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : "This trigger is limited to Tag Push event. Please edit your trigger on GitLab." } )
2022-09-05 10:20:33 +00:00
return
}
2022-09-05 09:00:46 +00:00
2022-09-05 10:20:33 +00:00
hook := GitLabWebhook { }
if err := c . ShouldBindJSON ( & hook ) ; err != nil {
2022-09-05 09:00:46 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
}
2022-09-05 10:20:33 +00:00
// Check token
repos , err := getRepositoriesByURI ( hook . Repository . URL )
if err != nil {
log . Println ( "Unable to getRepositoriesByURI:" , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to retrieve repositories." } )
return
}
var repo * Repository
for _ , r := range repos {
if len ( r . Secret ) == 0 || base64 . StdEncoding . EncodeToString ( r . Secret ) == c . Request . Header . Get ( "X-Gitlab-Token" ) {
repo = r
break
}
}
if repo == nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "There are no repositories matching this URL and secret. Please check the Gitlab Secret and retry pushing your tag." } )
return
}
2023-03-05 02:57:37 +00:00
work , err := getWork ( repo . IdWork )
2022-09-05 10:20:33 +00:00
if err != nil {
log . Println ( "Unable to getWork:" , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to find related work." } )
return
}
user , err := getUser ( int ( repo . IdUser ) )
if err != nil {
log . Println ( "Unable to getUser:" , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to find related user." } )
return
}
tmp := strings . SplitN ( hook . Ref , "/" , 3 )
if len ( tmp ) != 3 {
2022-11-11 12:27:09 +00:00
TriggerTagUpdate ( c , work , repo , user , nil , false )
2022-09-05 10:20:33 +00:00
return
}
if ! strings . HasPrefix ( tmp [ 2 ] , work . Tag ) {
2022-09-30 10:40:48 +00:00
// Allow to use a secret for another tag
if len ( repos ) > 1 {
for _ , r := range repos {
2023-03-05 02:57:37 +00:00
w , err := getWork ( r . IdWork )
2022-09-30 10:40:48 +00:00
if err != nil {
log . Println ( "Unable to getWork:" , err . Error ( ) )
continue
}
if strings . HasPrefix ( tmp [ 2 ] , w . Tag ) {
repo = r
work = w
break
}
}
}
if ! strings . HasPrefix ( tmp [ 2 ] , work . Tag ) {
c . AbortWithStatusJSON ( http . StatusOK , gin . H { "errmsg" : fmt . Sprintf ( "Ignore ref %q has it doesn't start with %s. Check submission instructions if this is not expected." , tmp [ 2 ] , work . Tag ) } )
return
}
2022-09-05 10:20:33 +00:00
}
2022-09-05 09:00:46 +00:00
2022-11-11 12:27:09 +00:00
TriggerTagUpdate ( c , work , repo , user , & tmp [ 2 ] , false )
2022-09-05 09:00:46 +00:00
} )
2023-03-05 02:57:58 +00:00
router . POST ( "/callbacks/tests.json" , func ( c * gin . Context ) {
// Check auth token
if c . GetHeader ( "X-Authorization" ) != testsCallbackToken {
c . AbortWithStatusJSON ( http . StatusUnauthorized , gin . H { "errmsg" : "Authorization token is invalid" } )
return
}
// Get form data
hook := TestsWebhook { }
if err := c . ShouldBindJSON ( & hook ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
}
// Retrieve corresponding repository
repo , err := getRepository ( hook . RepositoryId )
if err != nil {
log . Printf ( "Unable to getRepository(%d): %s" , hook . RepositoryId , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to retrieve repository." } )
return
}
2023-03-06 21:34:43 +00:00
err = hook . fetchRepoTests ( repo )
2023-03-05 02:57:58 +00:00
if err != nil {
log . Printf ( "Unable to fetchRepoTests(%d): %s" , hook . RepositoryId , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to fetch tests results." } )
return
}
} )
2022-09-05 09:00:46 +00:00
}
2022-09-04 09:38:10 +00:00
func repositoryHandler ( c * gin . Context ) {
var u * User
if user , ok := c . Get ( "user" ) ; ok {
u = user . ( * User )
} else {
u = c . MustGet ( "LoggedUser" ) . ( * User )
}
if rid , err := strconv . Atoi ( string ( c . Param ( "rid" ) ) ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : "Bad repository identifier." } )
return
2022-09-16 09:37:55 +00:00
} else if c . MustGet ( "LoggedUser" ) . ( * User ) . IsAdmin {
2022-09-04 09:38:10 +00:00
if repository , err := getRepository ( rid ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Repository not found." } )
return
} else {
c . Set ( "repository" , repository )
c . Next ( )
}
} else if repository , err := u . getRepository ( rid ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Repository not found." } )
return
} else {
c . Set ( "repository" , repository )
c . Next ( )
}
}
2022-11-11 12:27:09 +00:00
func TriggerTagUpdate ( c * gin . Context , work * Work , repo * Repository , u * User , tag * string , sig_optional bool ) {
2022-09-21 15:10:35 +00:00
loggeduser := c . MustGet ( "LoggedUser" ) . ( * User )
2022-09-05 10:20:33 +00:00
now := time . Now ( )
2022-09-21 15:10:35 +00:00
if ! loggeduser . IsAdmin && ( ! work . Shown || work . Corrected || work . StartAvailability . After ( now ) || work . EndAvailability . Add ( time . Hour ) . Before ( now ) ) {
2022-09-05 10:20:33 +00:00
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "The submission is closed." } )
return
}
repo_tag := work . Tag
if tag != nil {
repo_tag = * tag
}
2022-09-16 12:41:48 +00:00
login := u . Login
2022-10-17 21:48:06 +00:00
groups := u . Groups
2022-09-16 12:41:48 +00:00
if u . Id != repo . IdUser {
user , _ := getUser ( int ( repo . IdUser ) )
if user != nil {
login = user . Login
2022-10-17 21:48:06 +00:00
groups = user . Groups
2022-09-16 12:41:48 +00:00
}
}
2022-11-11 12:27:09 +00:00
env := map [ string ] string {
2022-09-10 10:14:11 +00:00
"REPO_URL" : repo . URI ,
"REPO_TAG" : repo_tag ,
2022-09-16 12:41:48 +00:00
"LOGIN" : login ,
2022-10-17 21:48:06 +00:00
"GROUPS" : groups ,
2022-09-10 10:14:11 +00:00
"DEST" : fmt . Sprintf ( "%d" , work . Id ) ,
2022-11-11 12:27:09 +00:00
}
if sig_optional {
env [ "TAG_SIG_OPTIONAL" ] = "1"
}
client := drone . NewClient ( droneEndpoint , droneConfig )
2022-12-15 16:32:00 +00:00
result , err := client . BuildCreate ( "teach" , "atsebay.t-worker" , "" , "master" , env )
2022-09-05 10:20:33 +00:00
if err != nil {
log . Println ( "Unable to communicate with Drone:" , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to communication with the extraction service." } )
return
}
2022-12-15 16:32:00 +00:00
repo . DroneRef = fmt . Sprintf ( "%s/%s/%d" , "teach" , "atsebay.t-worker" , result . Number )
2022-09-05 10:20:33 +00:00
repo . LastCheck = & now
repo . Update ( )
repo . Secret = [ ] byte { }
c . JSON ( http . StatusOK , repo )
}
2023-01-01 20:11:20 +00:00
func TriggerTests ( c * gin . Context , work * Work , repo * Repository , u * User ) {
if work . GradationRepo == nil || len ( * work . GradationRepo ) == 0 {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : "No tests defined for this work." } )
return
}
slug := strings . SplitN ( * work . GradationRepo , "/" , 2 )
if len ( slug ) != 2 {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : "Graduation repository is invalid." } )
return
}
login := u . Login
groups := u . Groups
if u . Id != repo . IdUser {
user , _ := getUser ( int ( repo . IdUser ) )
if user != nil {
login = user . Login
groups = user . Groups
}
}
branch := "master"
if len ( work . Tag ) > 0 {
branch = work . Tag
}
if branch [ len ( branch ) - 1 ] == '-' {
branch += "grades"
}
s , err := s3NewSession ( )
if err != nil {
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Something goes wrong." } )
return
}
req , _ := s3 . New ( s ) . GetObjectRequest ( & s3 . GetObjectInput {
Bucket : aws . String ( s3_bucket ) ,
2023-03-05 02:57:58 +00:00
Key : aws . String ( filepath . Join ( fmt . Sprintf ( "%d" , work . Id ) , fmt . Sprintf ( "rendu-%s.tar.xz" , login ) ) ) ,
2023-01-01 20:11:20 +00:00
} )
2023-03-05 16:05:01 +00:00
url , err := req . Presign ( SharingTime * 20 )
2023-01-01 20:11:20 +00:00
if err != nil {
log . Println ( "Unable to create presign URL:" , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Something goes wrong when creating the presigned URL." } )
return
}
env := map [ string ] string {
"SUBMISSION_URL" : repo . URI ,
2023-03-05 02:57:58 +00:00
"REPO_ID" : fmt . Sprintf ( "%d" , repo . Id ) ,
2023-01-01 20:11:20 +00:00
"META_URL" : url ,
"LOGIN" : login ,
"GROUPS" : groups ,
"DEST" : fmt . Sprintf ( "%d" , work . Id ) ,
}
client := drone . NewClient ( droneEndpoint , droneConfig )
result , err := client . BuildCreate ( slug [ 0 ] , slug [ 1 ] , "" , branch , env )
if err != nil {
log . Println ( "Unable to communicate with Drone:" , err . Error ( ) )
2023-03-05 02:57:58 +00:00
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Unable to communication with the gradation service." } )
2023-01-01 20:11:20 +00:00
return
}
now := time . Now ( )
repo . TestsRef = fmt . Sprintf ( "%s/%d" , * work . GradationRepo , result . Number )
repo . LastTests = & now
repo . Update ( )
repo . Secret = [ ] byte { }
c . JSON ( http . StatusOK , repo )
}
2023-03-05 17:16:38 +00:00
func ( w * Work ) stopTests ( ) error {
repos , err := w . GetRepositories ( )
if err != nil {
return err
}
client := drone . NewClient ( droneEndpoint , droneConfig )
for _ , repo := range repos {
slug := strings . Split ( repo . TestsRef , "/" )
if len ( slug ) < 3 {
continue
}
buildn , err := strconv . ParseInt ( slug [ 2 ] , 10 , 32 )
if err != nil {
continue
}
build , err := client . Build ( slug [ 0 ] , slug [ 1 ] , int ( buildn ) )
if err != nil {
log . Println ( "Unable to communicate with Drone:" , err . Error ( ) )
continue
}
if build . Status == "pending" {
err := client . BuildCancel ( slug [ 0 ] , slug [ 1 ] , int ( buildn ) )
if err != nil {
log . Println ( "Unable to cancel the build:" , err . Error ( ) )
continue
}
}
}
return nil
}
2022-09-04 09:38:10 +00:00
type Repository struct {
2022-09-30 10:41:34 +00:00
Id int64 ` json:"id" `
IdUser int64 ` json:"id_user" `
IdWork int64 ` json:"id_work" `
URI string ` json:"uri" `
Secret [ ] byte ` json:"secret,omitempty" `
LastCheck * time . Time ` json:"last_check" `
DroneRef string ` json:"drone_ref,omitempty" `
2023-01-01 18:32:32 +00:00
LastTests * time . Time ` json:"last_tests" `
2023-01-01 20:11:20 +00:00
TestsRef string ` json:"tests_ref,omitempty" `
2022-09-30 10:41:34 +00:00
AlreadyUsed bool ` json:"already_used,omitempty" `
2022-09-04 09:38:10 +00:00
}
func ( u * User ) GetRepositories ( ) ( repositories [ ] * Repository , err error ) {
2023-01-01 18:32:32 +00:00
if rows , errr := DBQuery ( "SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_user=?" , u . Id ) ; errr != nil {
2022-09-05 10:20:33 +00:00
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var repo Repository
2023-01-01 18:32:32 +00:00
if err = rows . Scan ( & repo . Id , & repo . IdUser , & repo . IdWork , & repo . URI , & repo . Secret , & repo . LastCheck , & repo . DroneRef , & repo . LastTests , & repo . TestsRef ) ; err != nil {
2022-09-05 10:20:33 +00:00
return
}
repositories = append ( repositories , & repo )
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2023-03-05 17:16:38 +00:00
func ( w * Work ) GetRepositories ( ) ( repositories [ ] * Repository , err error ) {
if rows , errr := DBQuery ( "SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_work=?" , w . Id ) ; errr != nil {
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var repo Repository
if err = rows . Scan ( & repo . Id , & repo . IdUser , & repo . IdWork , & repo . URI , & repo . Secret , & repo . LastCheck , & repo . DroneRef , & repo . LastTests , & repo . TestsRef ) ; err != nil {
return
}
repositories = append ( repositories , & repo )
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2022-09-05 10:20:33 +00:00
func getRepositoriesByURI ( uri string ) ( repositories [ ] * Repository , err error ) {
2023-01-01 18:32:32 +00:00
if rows , errr := DBQuery ( "SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE uri=?" , uri ) ; errr != nil {
2022-09-04 09:38:10 +00:00
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var repo Repository
2023-01-01 18:32:32 +00:00
if err = rows . Scan ( & repo . Id , & repo . IdUser , & repo . IdWork , & repo . URI , & repo . Secret , & repo . LastCheck , & repo . DroneRef , & repo . LastTests , & repo . TestsRef ) ; err != nil {
2022-09-04 09:38:10 +00:00
return
}
repositories = append ( repositories , & repo )
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
func getRepository ( id int ) ( r * Repository , err error ) {
r = new ( Repository )
2023-01-01 18:32:32 +00:00
err = DBQueryRow ( "SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_repository=?" , id ) . Scan ( & r . Id , & r . IdUser , & r . IdWork , & r . URI , & r . Secret , & r . LastCheck , & r . DroneRef , & r . LastTests , & r . TestsRef )
2022-09-04 09:38:10 +00:00
return
}
func ( u * User ) getRepository ( id int ) ( r * Repository , err error ) {
r = new ( Repository )
2023-01-01 18:32:32 +00:00
err = DBQueryRow ( "SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_repository=? AND id_user=?" , id , u . Id ) . Scan ( & r . Id , & r . IdUser , & r . IdWork , & r . URI , & r . Secret , & r . LastCheck , & r . DroneRef , & r . LastTests , & r . TestsRef )
2022-09-04 09:38:10 +00:00
return
}
2023-03-05 13:20:40 +00:00
func ( u * User ) getRepositoryByWork ( id int64 ) ( r * Repository , err error ) {
r = new ( Repository )
err = DBQueryRow ( "SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_work=? AND id_user=? ORDER BY last_tests DESC LIMIT 1" , id , u . Id ) . Scan ( & r . Id , & r . IdUser , & r . IdWork , & r . URI , & r . Secret , & r . LastCheck , & r . DroneRef , & r . LastTests , & r . TestsRef )
return
}
2022-09-04 09:38:10 +00:00
func ( u * User ) NewRepository ( w * Work , uri string ) ( * Repository , error ) {
2022-09-05 10:20:33 +00:00
secret := make ( [ ] byte , 24 )
_ , err := rand . Read ( secret )
if err != nil {
return nil , err
}
2023-03-06 00:46:38 +00:00
if res , err := DBExec ( "INSERT INTO user_work_repositories (id_user, id_work, uri, secret, droneref, testsref) VALUES (?, ?, ?, ?, ?, '')" , u . Id , w . Id , uri , secret , "" ) ; err != nil {
2022-09-04 09:38:10 +00:00
return nil , err
} else if rid , err := res . LastInsertId ( ) ; err != nil {
return nil , err
} else {
2023-01-01 20:11:20 +00:00
return & Repository { rid , u . Id , w . Id , uri , secret , nil , "" , nil , "" , false } , nil
2022-09-04 09:38:10 +00:00
}
}
func ( r * Repository ) Update ( ) ( * Repository , error ) {
2023-01-01 18:32:32 +00:00
if _ , err := DBExec ( "UPDATE user_work_repositories SET id_user = ?, id_work = ?, uri = ?, secret = ?, last_check = ?, droneref = ?, last_tests = ?, testsref = ? WHERE id_repository = ?" , r . IdUser , r . IdWork , r . URI , r . Secret , r . LastCheck , r . DroneRef , r . LastTests , r . TestsRef , r . Id ) ; err != nil {
2022-09-04 09:38:10 +00:00
return nil , err
} else {
return r , err
}
}
func ( r Repository ) Delete ( ) ( int64 , error ) {
if res , err := DBExec ( "DELETE FROM user_work_repositories WHERE id_repository = ?" , r . Id ) ; err != nil {
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
return nb , err
}
}
func ClearRepositories ( ) ( int64 , error ) {
if res , err := DBExec ( "DELETE FROM user_work_repositories" ) ; err != nil {
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
return nb , err
}
}