Refactor submission retrieval

This commit is contained in:
nemunaire 2022-09-05 04:40:49 +02:00
parent 24d39dd14e
commit eacaedeb03
7 changed files with 175 additions and 113 deletions

View File

@ -1,21 +1,14 @@
package main package main
import ( import (
"encoding/json"
"flag" "flag"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"os"
"path"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/drone/drone-go/drone" "github.com/drone/drone-go/drone"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"golang.org/x/oauth2" "golang.org/x/oauth2"
@ -25,35 +18,11 @@ var (
droneToken = "" droneToken = ""
droneConfig *http.Client droneConfig *http.Client
droneEndpoint string droneEndpoint string
s3_endpoint string
s3_region = "us"
s3_bucket string
s3_access_key string
s3_secret_key string
s3_path_style bool
) )
func init() { func init() {
flag.StringVar(&droneToken, "drone-token", droneToken, "Token for Drone Oauth") flag.StringVar(&droneToken, "drone-token", droneToken, "Token for Drone Oauth")
flag.StringVar(&droneEndpoint, "drone-endpoint", droneEndpoint, "Drone Endpoint") flag.StringVar(&droneEndpoint, "drone-endpoint", droneEndpoint, "Drone Endpoint")
s3_endpoint, _ = os.LookupEnv("S3_ENDPOINT")
if region, ok := os.LookupEnv("S3_REGION"); ok {
s3_region = region
}
s3_bucket, _ = os.LookupEnv("S3_BUCKET")
s3_access_key, _ = os.LookupEnv("S3_ACCESS_KEY")
s3_secret_key, _ = os.LookupEnv("S3_SECRET_KEY")
if path_style, ok := os.LookupEnv("S3_PATH_STYLE"); ok {
s3_path_style = path_style == "1" || path_style == "ON" || path_style == "on" || path_style == "TRUE" || path_style == "true" || path_style == "yes" || path_style == "YES"
}
flag.StringVar(&s3_endpoint, "s3-endpoint", s3_endpoint, "When using S3 backend, endpoint to use")
flag.StringVar(&s3_region, "s3-region", s3_region, "When using S3 backend, region to use")
flag.StringVar(&s3_bucket, "s3-bucket", s3_bucket, "When using S3 backend, bucket to use")
flag.StringVar(&s3_access_key, "s3-access-key", s3_access_key, "When using S3 backend, Access Key")
flag.StringVar(&s3_secret_key, "s3-secret-key", s3_secret_key, "When using S3 backend, Secret Key")
flag.BoolVar(&s3_path_style, "s3-path-style", s3_path_style, "When using S3 backend, force path style (when using minio)")
} }
func initializeDroneOauth() { func initializeDroneOauth() {
@ -68,15 +37,6 @@ func initializeDroneOauth() {
} }
} }
func s3NewSession() (*session.Session, error) {
return session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials(s3_access_key, s3_secret_key, ""),
Endpoint: aws.String(s3_endpoint),
Region: aws.String(s3_region),
S3ForcePathStyle: &s3_path_style,
})
}
func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) { func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
router.GET("/repositories", func(c *gin.Context) { router.GET("/repositories", func(c *gin.Context) {
var u *User var u *User
@ -196,10 +156,10 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
now := time.Now() now := time.Now()
/*if repo.LastCheck != nil && !repo.LastCheck.Before(now.Add(-5*time.Minute)) { if repo.LastCheck != nil && !repo.LastCheck.Before(now.Add(-5*time.Minute)) {
c.AbortWithStatusJSON(http.StatusPaymentRequired, gin.H{"errmsg": "Please wait between two pulls."}) c.AbortWithStatusJSON(http.StatusPaymentRequired, gin.H{"errmsg": "Please wait between two pulls."})
return return
}*/ }
client := drone.NewClient(droneEndpoint, droneConfig) client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.BuildCreate("srs", "atsebay.t-worker", "", "master", map[string]string{ result, err := client.BuildCreate("srs", "atsebay.t-worker", "", "master", map[string]string{
@ -225,6 +185,11 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
repo := c.MustGet("repository").(*Repository) repo := c.MustGet("repository").(*Repository)
tmp := strings.Split(repo.DroneRef, "/") 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]) nbuild, err := strconv.Atoi(tmp[2])
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Bad build number. Please retry pulling your work."}) c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Bad build number. Please retry pulling your work."})
@ -239,49 +204,6 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
} }
c.JSON(http.StatusOK, result) c.JSON(http.StatusOK, result)
})
repositoriesRoutes.GET("/submission", func(c *gin.Context) {
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = c.MustGet("LoggedUser").(*User)
}
repo := c.MustGet("repository").(*Repository)
work, err := getWork(int(repo.IdWork))
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
return
}
s, err := s3NewSession()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong."})
return
}
log.Println(path.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.metadata", u.Login)))
result, err := s3.New(s).GetObject(&s3.GetObjectInput{
Bucket: aws.String(s3_bucket),
Key: aws.String(path.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.metadata", u.Login))),
})
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Submission not found."})
return
}
tmp := map[string]interface{}{}
err = json.NewDecoder(result.Body).Decode(&tmp)
if err != nil {
log.Println("Unable to decode JSON metadata:", err.Error())
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Submission not found."})
return
}
c.JSON(http.StatusOK, tmp)
}) })
} }

125
submissions.go Normal file
View File

@ -0,0 +1,125 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"path"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/gin-gonic/gin"
)
const SharingTime = 15 * time.Minute
var (
s3_endpoint string
s3_region = "us"
s3_bucket string
s3_access_key string
s3_secret_key string
s3_path_style bool
)
func init() {
s3_endpoint, _ = os.LookupEnv("S3_ENDPOINT")
if region, ok := os.LookupEnv("S3_REGION"); ok {
s3_region = region
}
s3_bucket, _ = os.LookupEnv("S3_BUCKET")
s3_access_key, _ = os.LookupEnv("S3_ACCESS_KEY")
s3_secret_key, _ = os.LookupEnv("S3_SECRET_KEY")
if path_style, ok := os.LookupEnv("S3_PATH_STYLE"); ok {
s3_path_style = path_style == "1" || path_style == "ON" || path_style == "on" || path_style == "TRUE" || path_style == "true" || path_style == "yes" || path_style == "YES"
}
flag.StringVar(&s3_endpoint, "s3-endpoint", s3_endpoint, "When using S3 backend, endpoint to use")
flag.StringVar(&s3_region, "s3-region", s3_region, "When using S3 backend, region to use")
flag.StringVar(&s3_bucket, "s3-bucket", s3_bucket, "When using S3 backend, bucket to use")
flag.StringVar(&s3_access_key, "s3-access-key", s3_access_key, "When using S3 backend, Access Key")
flag.StringVar(&s3_secret_key, "s3-secret-key", s3_secret_key, "When using S3 backend, Secret Key")
flag.BoolVar(&s3_path_style, "s3-path-style", s3_path_style, "When using S3 backend, force path style (when using minio)")
}
func s3NewSession() (*session.Session, error) {
return session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials(s3_access_key, s3_secret_key, ""),
Endpoint: aws.String(s3_endpoint),
Region: aws.String(s3_region),
S3ForcePathStyle: &s3_path_style,
})
}
func declareAPIWorkSubmissionsRoutes(router *gin.RouterGroup) {
router.GET("/submission", func(c *gin.Context) {
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = c.MustGet("LoggedUser").(*User)
}
work := c.MustGet("work").(*Work)
s, err := s3NewSession()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong."})
return
}
result, err := s3.New(s).GetObject(&s3.GetObjectInput{
Bucket: aws.String(s3_bucket),
Key: aws.String(path.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.metadata", u.Login))),
})
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Submission not found."})
return
}
tmp := map[string]interface{}{}
err = json.NewDecoder(result.Body).Decode(&tmp)
if err != nil {
log.Println("Unable to decode JSON metadata:", err.Error())
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Submission not found."})
return
}
c.JSON(http.StatusOK, tmp)
})
router.GET("/download", func(c *gin.Context) {
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = c.MustGet("LoggedUser").(*User)
}
work := c.MustGet("work").(*Work)
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),
Key: aws.String(path.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.tar.xz", u.Login))),
})
url, err := req.Presign(SharingTime)
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
}
c.Redirect(http.StatusFound, url)
})
}

View File

@ -70,14 +70,6 @@
{:else} {:else}
{state.status} {state.status}
{/if} {/if}
{/await}<br>
Rendu&nbsp;:
{#await repo.getSubmission()}
<div class="spinner-grow spinner-grow-sm mx-1" role="status"></div>
{:then submission}
<strong>{submission.commit} ({submission.tag})</strong> (taille&nbsp;: {submission.size}&nbsp;o, date&nbsp;: <DateFormat date={new Date(submission.date)} dateStyle="medium" timeStyle="medium" />)
{:catch}
<strong>-</strong>
{/await} {/await}
</div> </div>
</div> </div>
@ -88,7 +80,7 @@
disable={submitInProgress || readonly} disable={submitInProgress || readonly}
on:click={() => repo.retrieveWork().then(() => { refresh_repo_work() }, (error) => ToastsStore.addErrorToast({msg: "Une erreur s'est produite : " + error}))} on:click={() => repo.retrieveWork().then(() => { refresh_repo_work() }, (error) => ToastsStore.addErrorToast({msg: "Une erreur s'est produite : " + error}))}
> >
Mettre à jour Récupérer mon travail
</button> </button>
<button <button
type="button" type="button"

View File

@ -36,17 +36,6 @@ export class WorkRepository {
} }
} }
async getSubmission() {
const res = await fetch(`api/repositories/${this.id}/submission`, {
headers: {'Accept': 'application/json'}
});
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
async retrieveWork() { async retrieveWork() {
const res = await fetch(`api/repositories/${this.id}/trigger`, { const res = await fetch(`api/repositories/${this.id}/trigger`, {
method: 'POST', method: 'POST',

View File

@ -91,6 +91,17 @@ export class Work {
} }
} }
async getSubmission() {
const res = await fetch(`api/works/${this.id}/submission`, {
headers: {'Accept': 'application/json'}
});
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
async getGrades() { async getGrades() {
const res = await fetch(`api/works/${this.id}/grades`, { const res = await fetch(`api/works/${this.id}/grades`, {
method: 'GET', method: 'GET',

View File

@ -125,11 +125,33 @@
{/if} {/if}
<WorkRepository class="mb-3" readonly={w.corrected || new Date(w.end_availability) <= new Date()} work={w} /> <WorkRepository class="mb-3" readonly={w.corrected || new Date(w.end_availability) <= new Date()} work={w} />
{#if w.submission_url} <div class="card mb-3">
<p> <div class="card-body d-flex justify-content-between">
<strong>État du rendu&nbsp;:</strong> <SubmissionStatus work={w} /> <div>
</p> {#if w.submission_url}
{/if} <strong>État du rendu&nbsp;:</strong> <SubmissionStatus work={w} />
{:else}
Rendu&nbsp;:
{#await w.getSubmission()}
<div class="spinner-grow spinner-grow-sm mx-1" role="status"></div>
{:then submission}
<strong>{submission.commit} ({submission.tag})</strong> (taille&nbsp;: {submission.size}&nbsp;o, date&nbsp;: <DateFormat date={new Date(submission.date)} dateStyle="medium" timeStyle="medium" />)
{:catch}
<strong>-</strong>
{/await}
{/if}
</div>
<div class="d-flex flex-column justify-content-center">
<a
href="/api/works/{w.id}/download"
class="btn btn-sm btn-dark"
title="Voir la tarball de mon rendu"
>
<i class="bi bi-download"></i>
</a>
</div>
</div>
</div>
{#if w.corrected} {#if w.corrected}
{#await getScore(w)} {#await getScore(w)}
<div class="spinner-border spinner-border-sm" role="status"></div> <div class="spinner-border spinner-border-sm" role="status"></div>
@ -145,9 +167,9 @@
</div> </div>
{/await} {/await}
{:else} {:else}
<p> <div class="alert alert-warning">
Pour être reconnu, vous devez pousser un tag <strong><a href="keys">signé</a></strong> sur votre dépôt. {#if w.tag}Le tag attendu doit commencer par&nbsp;: <code>{w.tag}</code>. Par exemple <code>{w.tag}v1.0</code>, <code>{w.tag}v1.1</code>, ...{/if} Seul le dernier tag <strong>alphabétique</strong> que vous envoyez avant la date du rendu sera pris en compte. Vous pouvez donc faire autant de tag que vous le souhaitez d'ici la date du rendu. Pour être reconnu, vous devez pousser un tag <strong><a href="keys">signé</a></strong> sur votre dépôt. {#if w.tag}Le tag attendu doit commencer par&nbsp;: <code>{w.tag}</code>. Par exemple <code>{w.tag}v1.0</code>, <code>{w.tag}v1.1</code>, &hellip;{/if} Seul le dernier tag <strong>alphabétique</strong> que vous envoyez avant la date du rendu sera pris en compte. Vous pouvez donc faire autant de tag que vous le souhaitez d'ici la date du rendu.
</p> </div>
{/if} {/if}
{/if} {/if}
{/await} {/await}

View File

@ -220,6 +220,7 @@ func declareAPIAuthWorksRoutes(router *gin.RouterGroup) {
}) })
declareAPIAuthRepositoriesRoutes(worksRoutes) declareAPIAuthRepositoriesRoutes(worksRoutes)
declareAPIWorkSubmissionsRoutes(worksRoutes)
} }
func workHandler(c *gin.Context) { func workHandler(c *gin.Context) {