package template
import (
"fmt"
"os"
"os/exec"
"path"
"syscall"
"go-create/internal/copyFile"
)
const gtkBasePath = "/home/per/code/templates/gtk-startup"
type gtkTemplate struct {
project *Project
}
func (t *gtkTemplate) create() {
t.createProjectFolders()
t.copyProjectFiles()
t.goMod()
t.gitInit()
}
func (t *gtkTemplate) createProjectFolders() {
// Create main project directory
createFolder(t.project.Path)
// Create project folders
createFolder(path.Join(t.project.Path, "assets"))
createFolder(path.Join(t.project.Path, "build"))
createFolder(path.Join(t.project.Path, "cmd"))
createFolder(path.Join(t.project.Path, "cmd", t.project.Name))
createFolder(path.Join(t.project.Path, "internal"))
createFolder(path.Join(t.project.Path, "internal", t.project.Name))
createFolder(path.Join(t.project.Path, ".run"))
}
func (t *gtkTemplate) copyProjectFiles() {
// BASE FILES
cfo := ©File.CopyFileOperation{
From: ©File.CopyFilePath{BasePath: gtkBasePath},
To: ©File.CopyFilePath{BasePath: t.project.Path},
ProjectName: t.project.Name,
Description: t.project.Description,
}
cfo.SetFileName(".gitignore")
cfo.CopyFile()
cfo.SetFileName("readme.md")
cfo.CopyFile()
// ASSETS
cfo.SetRelativePath("assets")
cfo.SetFileName("application.png")
cfo.CopyFile()
cfo.SetFileName("main.glade")
cfo.CopyFile()
// MAIN FILES
cfo.SetFileName("main.go")
cfo.From.RelativePath = "cmd/gtk-startup"
cfo.To.RelativePath = fmt.Sprintf("cmd/%s", t.project.Name)
cfo.CopyFile()
// INTERNAL FILES
cfo.From.RelativePath = "internal/gtk-startup"
cfo.To.RelativePath = fmt.Sprintf("internal/%s", t.project.Name)
cfo.SetFileName("mainForm.go")
cfo.CopyFile()
cfo.SetFileName("extraForm.go")
cfo.CopyFile()
cfo.SetFileName("dialog.go")
cfo.CopyFile()
cfo.SetFileName("aboutDialog.go")
cfo.CopyFile()
// RUN CONFIGURATION
cfo.SetRelativePath(".run")
cfo.From.FileName = "project-name.run.xml"
cfo.To.FileName = fmt.Sprintf("%s.run.xml", t.project.Name)
cfo.CopyFile()
}
func (t *gtkTemplate) goMod() {
fmt.Printf("Running : go mod init github.com/hultan/%s...\n", t.project.Name)
command := fmt.Sprintf("cd %s;go mod init github.com/hultan/%s", t.project.Path, t.project.Name)
cmd := exec.Command("bash", "-c", command)
// Forces the new process to detach from the GitDiscover process
// so that it does not die when GitDiscover dies
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
output, err := cmd.CombinedOutput()
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to run : go mod init github.com/hultan/%s : %v", t.project.Name, err)
}
err = cmd.Process.Release()
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to release process (goMod) : %v", err)
}
fmt.Println(string(output))
}
func (t *gtkTemplate) gitInit() {
fmt.Println("Running : git init...")
command := fmt.Sprintf("cd %s;git init", t.project.Path)
cmd := exec.Command("bash", "-c", command)
// Forces the new process to detach from the GitDiscover process
// so that it does not die when GitDiscover dies
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
output, err := cmd.CombinedOutput()
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to run : git init : %v", err)
}
err = cmd.Process.Release()
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to release process (gitInit): %v", err)
}
fmt.Println(string(output))
}
package template
import (
"fmt"
"os"
"os/exec"
"path"
"syscall"
"go-create/internal/copyFile"
)
const normalBasePath = "/home/per/code/templates/normal"
type normalTemplate struct {
project *Project
}
func (t *normalTemplate) create() {
t.createProjectFolders()
t.copyProjectFiles()
t.goMod()
t.gitInit()
}
func (t *normalTemplate) createProjectFolders() {
// Create main project directory
createFolder(t.project.Path)
// Create project folders
createFolder(path.Join(t.project.Path, "assets"))
createFolder(path.Join(t.project.Path, "build"))
createFolder(path.Join(t.project.Path, "cmd"))
createFolder(path.Join(t.project.Path, "cmd", t.project.Name))
createFolder(path.Join(t.project.Path, "internal"))
createFolder(path.Join(t.project.Path, "internal", t.project.Name))
createFolder(path.Join(t.project.Path, ".run"))
}
func (t *normalTemplate) copyProjectFiles() {
// BASE FILES
cfo := ©File.CopyFileOperation{
From: ©File.CopyFilePath{BasePath: normalBasePath},
To: ©File.CopyFilePath{BasePath: t.project.Path},
ProjectName: t.project.Name,
Description: t.project.Description,
}
cfo.SetFileName(".gitignore")
cfo.CopyFile()
cfo.SetFileName("readme.md")
cfo.CopyFile()
// ASSETS
cfo.SetRelativePath("assets")
cfo.SetFileName("application.png")
cfo.CopyFile()
// MAIN FILES
cfo.SetFileName("main.go")
cfo.From.RelativePath = "cmd/normal"
cfo.To.RelativePath = fmt.Sprintf("cmd/%s", t.project.Name)
cfo.CopyFile()
// INTERNAL FILES
cfo.From.RelativePath = "internal/normal"
cfo.To.RelativePath = fmt.Sprintf("internal/%s", t.project.Name)
cfo.SetFileName("normal.go")
cfo.CopyFile()
// RUN CONFIGURATION
cfo.SetRelativePath(".run")
cfo.From.FileName = "project-name.run.xml"
cfo.To.FileName = fmt.Sprintf("%s.run.xml", t.project.Name)
cfo.CopyFile()
}
func (t *normalTemplate) goMod() {
fmt.Printf("Running : go mod init github.com/hultan/%s...\n", t.project.Name)
command := fmt.Sprintf("cd %s;go mod init github.com/hultan/%s", t.project.Path, t.project.Name)
cmd := exec.Command("bash", "-c", command)
// Forces the new process to detach from the GitDiscover process
// so that it does not die when GitDiscover dies
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
output, err := cmd.CombinedOutput()
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to run : go mod init github.com/hultan/%s : %v", t.project.Name, err)
}
err = cmd.Process.Release()
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to release process (goMod) : %v", err)
}
fmt.Println(string(output))
}
func (t *normalTemplate) gitInit() {
fmt.Println("Running : git init...")
command := fmt.Sprintf("cd %s;git init", t.project.Path)
cmd := exec.Command("bash", "-c", command)
// Forces the new process to detach from the GitDiscover process
// so that it does not die when GitDiscover dies
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
output, err := cmd.CombinedOutput()
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to run : git init : %v", err)
}
err = cmd.Process.Release()
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to release process (gitInit): %v", err)
}
fmt.Println(string(output))
}
// Copyright 2019-2020 The Pythia Authors.
// This file is part of Pythia.
//
// Pythia is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Pythia is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pythia. If not, see <http://www.gnu.org/licenses/>.
package handler
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"strconv"
"strings"
"github.com/gorilla/mux"
"github.com/mitchellh/mapstructure"
"github.com/pythia-project/pythia-core/go/src/pythia"
"github.com/pythia-project/pythia-server/server"
)
// HealthHandler handles route /api/health
func HealthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
conn, err := pythia.Dial(server.Conf.Address.Queue)
if err == nil {
conn.Close()
}
info := server.HealthInfo{err == nil}
data, err := json.Marshal(info)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write(data)
}
// ExecuteHandler handles route /api/execute
func ExecuteHandler(w http.ResponseWriter, r *http.Request) {
request := server.SubmisssionRequest{}
err := json.NewDecoder(r.Body).Decode(&request)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
var async bool
if r.FormValue("async") == "" {
async = false
} else {
async, err = strconv.ParseBool(r.FormValue("async"))
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
}
executeTask(request, async, w)
}
// ListEnvironments lists all the available environments.
func ListEnvironments(w http.ResponseWriter, r *http.Request) {
files, err := ioutil.ReadDir(server.Conf.Path.Environments)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
environments := make([]server.Environment, 0)
for _, f := range files {
name := f.Name()
if strings.HasSuffix(name, ".sfs") {
environments = append(environments, server.Environment{Name: name[:len(name)-4]})
}
}
data, err := json.Marshal(environments)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(data)
}
// GetEnvironment retrieves one given environment.
func GetEnvironment(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
envpath := fmt.Sprintf("%s/%s.env", server.Conf.Path.Environments, vars["envid"])
if _, err := os.Stat(envpath); err == nil {
if content, err := ioutil.ReadFile(envpath); err == nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(content)
return
}
} else if os.IsNotExist(err) {
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusInternalServerError)
}
// ListTasks lists all the available tasks.
func ListTasks(w http.ResponseWriter, r *http.Request) {
files, err := ioutil.ReadDir(server.Conf.Path.Tasks)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
tasks := make([]server.Task, 0)
for _, f := range files {
name := f.Name()
if strings.HasSuffix(name, ".task") {
tasks = append(tasks, server.Task{Taskid: name[:len(name)-5]})
}
}
data, err := json.Marshal(tasks)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(data)
}
// CreateTask creates a new task.
func CreateTask(w http.ResponseWriter, r *http.Request) {
request := server.TaskCreationRequest{
Type: "raw",
Limits: server.Limits{
Time: 60,
Memory: 32,
Disk: 50,
Output: 1024,
},
}
err := json.NewDecoder(r.Body).Decode(&request)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
// Check whether a task with the same ID already exists
taskDir := fmt.Sprintf("%s/%s", server.Conf.Path.Tasks, request.Taskid)
taskFile := fmt.Sprintf("%s.task", taskDir)
if _, err := os.Stat(fmt.Sprintf("%s.task", taskDir)); err == nil {
log.Println("Task id", request.Taskid, "already exists.")
w.WriteHeader(http.StatusBadRequest)
return
}
// Create the task directory
if err := os.Mkdir(taskDir, 0755); err != nil {
log.Println("Impossible to create task directory:", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
// Create the task file
task := pythia.Task{
Environment: request.Environment,
TaskFS: request.Taskid + ".sfs",
Limits: request.Limits,
}
file, _ := json.MarshalIndent(task, "", " ")
_ = ioutil.WriteFile(taskFile, file, 0644)
// Copy the files from the template
switch request.Type {
case "input-output":
_ = os.Mkdir(taskDir+"/config", 0755)
_ = os.Mkdir(taskDir+"/scripts", 0755)
_ = os.Mkdir(taskDir+"/skeleton", 0755)
templateDir := "templates/input-output/" + request.Environment
_ = copyFile(templateDir+"/control", taskDir+"/control", 0755)
_ = copyFile(templateDir+"/scripts/pythia-iot", taskDir+"/scripts/pythia-iot", 0755)
switch request.Environment {
case "ada":
_ = copyFile(templateDir+"/scripts/execute.sh", taskDir+"/scripts/execute.sh", 0755)
_ = copyFile(templateDir+"/skeleton/program.adb", taskDir+"/skeleton/program.adb", 0755)
case "algol68":
_ = copyFile(templateDir+"/skeleton/program.alg", taskDir+"/skeleton/program.alg", 0755)
case "bash":
_ = copyFile(templateDir+"/skeleton/program.sh", taskDir+"/skeleton/program.sh", 0755)
case "c":
_ = copyFile(templateDir+"/scripts/execute.sh", taskDir+"/scripts/execute.sh", 0755)
_ = copyFile(templateDir+"/skeleton/program.c", taskDir+"/skeleton/program.c", 0755)
case "cpp":
_ = copyFile(templateDir+"/scripts/execute.sh", taskDir+"/scripts/execute.sh", 0755)
_ = copyFile(templateDir+"/skeleton/program.cpp", taskDir+"/skeleton/program.cpp", 0755)
case "golang":
_ = copyFile(templateDir+"/scripts/execute.sh", taskDir+"/scripts/execute.sh", 0755)
_ = copyFile(templateDir+"/skeleton/program.go", taskDir+"/skeleton/program.go", 0755)
case "java":
_ = copyFile(templateDir+"/scripts/execute.sh", taskDir+"/scripts/execute.sh", 0755)
_ = copyFile(templateDir+"/skeleton/Program.java", taskDir+"/skeleton/Program.java", 0755)
case "lua":
_ = copyFile(templateDir+"/skeleton/program.lua", taskDir+"/skeleton/program.lua", 0755)
case "nodejs":
_ = copyFile(templateDir+"/skeleton/program.js", taskDir+"/skeleton/program.js", 0755)
case "php7":
_ = copyFile(templateDir+"/skeleton/program.php", taskDir+"/skeleton/program.php", 0755)
case "prolog":
_ = copyFile(templateDir+"/skeleton/program.pl", taskDir+"/skeleton/program.pl", 0755)
case "python":
_ = copyFile(templateDir+"/skeleton/program.py", taskDir+"/skeleton/program.py", 0755)
case "rexx":
_ = copyFile(templateDir+"/skeleton/program.rexx", taskDir+"/skeleton/program.rexx", 0755)
case "rust":
_ = copyFile(templateDir+"/scripts/execute.sh", taskDir+"/scripts/execute.sh", 0755)
_ = copyFile(templateDir+"/skeleton/program.rs", taskDir+"/skeleton/program.rs", 0755)
case "tcl":
_ = copyFile(templateDir+"/skeleton/program.tcl", taskDir+"/skeleton/program.tcl", 0755)
}
// Save the configuration
config := server.InputOutputTaskConfig{}
if mapstructure.Decode(request.Config, &config) == nil {
file, _ = json.MarshalIndent(config, "", " ")
_ = ioutil.WriteFile(taskDir+"/config/test.json", file, 0644)
}
case "unit-testing":
_ = os.Mkdir(taskDir+"/config", 0755)
_ = os.Mkdir(taskDir+"/scripts", 0755)
_ = os.Mkdir(taskDir+"/skeleton", 0755)
_ = os.Mkdir(taskDir+"/static", 0755)
_ = os.Mkdir(taskDir+"/static/lib", 0755)
templateDir := "templates/unit-testing/" + request.Environment
_ = copyFile(templateDir+"/control", taskDir+"/control", 0755)
_ = copyFile(templateDir+"/scripts/pythia-utbt", taskDir+"/scripts/pythia-utbt", 0755)
switch request.Environment {
case "python":
_ = copyFile(templateDir+"/scripts/execute.py", taskDir+"/scripts/execute.py", 0755)
_ = copyFile(templateDir+"/static/lib/__init__.py", taskDir+"/static/lib/__init__.py", 0755)
_ = copyFile(templateDir+"/static/lib/pythia.py", taskDir+"/static/lib/pythia.py", 0755)
case "java":
_ = copyFile(templateDir+"/scripts/execute.sh", taskDir+"/scripts/execute.sh", 0755)
_ = copyFile(templateDir+"/static/lib/commons-csv-1.7.jar", taskDir+"/static/lib/commons-csv-1.7.jar", 0755)
_ = copyFile(templateDir+"/static/lib/json-20180813.jar", taskDir+"/static/lib/json-20180813.jar", 0755)
_ = copyFile(templateDir+"/static/lib/pythia-1.0.jar", taskDir+"/static/lib/pythia-1.0.jar", 0755)
}
// Save the configuration
config := server.UnitTestingTaskConfig{}
if mapstructure.Decode(request.Config, &config) == nil {
file, _ := json.MarshalIndent(config.Spec, "", " ")
_ = ioutil.WriteFile(taskDir+"/config/spec.json", file, 0644)
file, _ = json.MarshalIndent(config.Test, "", " ")
_ = ioutil.WriteFile(taskDir+"/config/test.json", file, 0644)
// Create skeletons files
content := ""
switch request.Environment {
case "python":
params := make([]string, 0)
for _, elem := range config.Spec.Args {
params = append(params, elem.Name)
}
content = fmt.Sprintf("# -*- coding: utf-8 -*-\[email protected]@[email protected]@\ndef %s(%s):\[email protected] @[email protected]@\n", config.Spec.Name, strings.Join(params, ", "))
ioutil.WriteFile(taskDir+"/skeleton/program.py", []byte(content), 0755)
case "java":
params := make([]string, 0)
for _, elem := range config.Spec.Args {
params = append(params, elem.Type+" "+elem.Name)
}
content = fmt.Sprintf("@@[email protected]@\n\npublic class Program\n{\n\tpublic static %s %s (%s)\n\t{\[email protected]\t\[email protected]@@\n\t}\n}\n", config.Spec.Return, config.Spec.Name, strings.Join(params, ", "))
ioutil.WriteFile(taskDir+"/skeleton/Program.java", []byte(content), 0755)
}
// Create solution file
file, _ = json.MarshalIndent(config.Solution, "", " ")
_ = ioutil.WriteFile(taskDir+"/config/solution.json", file, 0644)
}
}
// Compile the SFS
// mksquashfs TASK TASK.sfs -all-root -comp lzo -noappend
wd, _ := os.Getwd()
_ = os.Chdir(server.Conf.Path.Tasks)
exec.Command("mksquashfs", request.Taskid, request.Taskid+".sfs", "-all-root", "-comp", "lzo", "-noappend").Run()
_ = os.Chdir(wd)
w.WriteHeader(http.StatusOK)
}
// GetTask retrieves one given task.
func GetTask(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
taskpath := fmt.Sprintf("%s/%s.task", server.Conf.Path.Tasks, vars["taskid"])
if _, err := os.Stat(taskpath); err == nil {
if content, err := ioutil.ReadFile(taskpath); err == nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(content)
return
}
} else if os.IsNotExist(err) {
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusInternalServerError)
}
// DeleteTask deletes one given task.
func DeleteTask(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
taskdir := fmt.Sprintf("%s/%s", server.Conf.Path.Tasks, vars["taskid"])
if _, err := os.Stat(taskdir); err == nil {
_ = os.RemoveAll(taskdir)
_ = os.Remove(taskdir + ".sfs")
_ = os.Remove(taskdir + ".task")
w.WriteHeader(http.StatusOK)
return
} else if os.IsNotExist(err) {
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusInternalServerError)
}
// ExecuteTask executes one given task.
func ExecuteTask(w http.ResponseWriter, r *http.Request) {
request := server.SubmisssionRequest{}
err := json.NewDecoder(r.Body).Decode(&request)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
vars := mux.Vars(r)
taskpath := fmt.Sprintf("%s/%s.sfs", server.Conf.Path.Tasks, vars["taskid"])
if _, err := os.Stat(taskpath); err != nil {
if os.IsNotExist(err) {
w.WriteHeader(http.StatusNotFound)
return
}
}
request.Tid = vars["taskid"]
var async bool
if r.FormValue("async") == "" {
async = false
} else {
async, err = strconv.ParseBool(r.FormValue("async"))
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
}
executeTask(request, async, w)
}
func copyFile(src string, dst string, perms os.FileMode) (err error) {
var from, to *os.File
if from, err = os.Open(src); err == nil {
defer from.Close()
if to, err = os.OpenFile(dst, os.O_RDWR|os.O_CREATE, perms); err == nil {
defer to.Close()
_, err = io.Copy(to, from)
}
}
return
}
func executeTask(request server.SubmisssionRequest, async bool, w http.ResponseWriter) {
if async && request.Callback == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
// Connection to the pool and execution of the task
conn := pythia.DialRetry(server.Conf.Address.Queue)
var task pythia.Task
file, err := os.Open(fmt.Sprintf("%v/%v.task", server.Conf.Path.Tasks, request.Tid))
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
err = json.NewDecoder(file).Decode(&task)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
conn.Send(pythia.Message{
Message: pythia.LaunchMsg,
Id: "test",
Task: &task,
Input: request.Input,
})
receive := func() (res []byte, err error) {
msg, ok := <-conn.Receive()
if !ok {
err = errors.New("Pythia request failed")
return
}
result := server.SubmisssionResult{request.Tid, string(msg.Status), msg.Output}
res, err = json.Marshal(result)
if err != nil {
return
}
return
}
if async {
go func() {
byteData, err := receive()
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
conn.Close()
data := strings.NewReader(string(byteData))
postResponse, err := http.Post(request.Callback, "application/json", data)
if err != nil {
log.Println(err)
return
}
log.Println(postResponse)
}()
} else {
byteData, err := receive()
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
conn.Close()
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.WriteHeader(http.StatusOK)
w.Write(byteData)
}
}