feat(be): api/runners -> internal/runners

This commit is contained in:
Denis Gukov 2024-09-26 17:52:31 +05:00
parent 9817b345e6
commit dc565f3508
11 changed files with 85 additions and 97 deletions

View File

@ -87,7 +87,7 @@ func Route() *mux.Router {
publicAPIRouter.HandleFunc("/auth/oidc/{provider}/redirect", oidcRedirect).Methods("GET")
publicAPIRouter.HandleFunc("/auth/oidc/{provider}/redirect/{redirect_path:.*}", oidcRedirect).Methods("GET")
runnersAPI := r.PathPrefix(webPath + "api").Subrouter()
runnersAPI := r.PathPrefix(webPath + "internal").Subrouter()
runnersAPI.Use(StoreMiddleware, JSONMiddleware, runners.RunnerMiddleware)
runnersAPI.Path("/runners/{runner_id}").HandlerFunc(runners.GetRunner).Methods("GET", "HEAD")
runnersAPI.Path("/runners/{runner_id}").HandlerFunc(runners.UpdateRunner).Methods("PUT")
@ -128,10 +128,10 @@ func Route() *mux.Router {
adminAPI.Path("/options").HandlerFunc(getOptions).Methods("GET", "HEAD")
adminAPI.Path("/options").HandlerFunc(setOption).Methods("POST")
adminAPI.Path("/global-runners").HandlerFunc(getGlobalRunners).Methods("GET", "HEAD")
adminAPI.Path("/global-runners").HandlerFunc(addGlobalRunner).Methods("POST", "HEAD")
adminAPI.Path("/runners").HandlerFunc(getGlobalRunners).Methods("GET", "HEAD")
adminAPI.Path("/runners").HandlerFunc(addGlobalRunner).Methods("POST", "HEAD")
globalRunnersAPI := adminAPI.PathPrefix("/global-runners").Subrouter()
globalRunnersAPI := adminAPI.PathPrefix("/runners").Subrouter()
globalRunnersAPI.Use(globalRunnerMiddleware)
globalRunnersAPI.Path("/{runner_id}").HandlerFunc(getGlobalRunner).Methods("GET", "HEAD")
globalRunnersAPI.Path("/{runner_id}").HandlerFunc(updateGlobalRunner).Methods("PUT", "POST")

View File

@ -9,36 +9,32 @@ import (
"github.com/gorilla/context"
)
type minimalGlobalRunner struct {
ID int `json:"id"`
Name string `json:"name"`
Active bool `json:"active"`
Webhook string `db:"webhook" json:"webhook"`
MaxParallelTasks int `db:"max_parallel_tasks" json:"max_parallel_tasks"`
}
//type minimalGlobalRunner struct {
// ID int `json:"id"`
// Name string `json:"name"`
// Active bool `json:"active"`
// Webhook string `db:"webhook" json:"webhook"`
// MaxParallelTasks int `db:"max_parallel_tasks" json:"max_parallel_tasks"`
//}
func getGlobalRunners(w http.ResponseWriter, r *http.Request) {
runners, err := helpers.Store(r).GetGlobalRunners()
runners, err := helpers.Store(r).GetGlobalRunners(false)
if err != nil {
panic(err)
}
var result = make([]minimalGlobalRunner, 0)
var result = make([]db.Runner, 0)
for _, runner := range runners {
result = append(result, minimalGlobalRunner{
ID: runner.ID,
Name: "",
Active: false,
})
result = append(result, runner)
}
helpers.WriteJSON(w, http.StatusOK, result)
}
func addGlobalRunner(w http.ResponseWriter, r *http.Request) {
var runner minimalGlobalRunner
var runner db.Runner
if !helpers.Bind(w, r, &runner) {
return
}
@ -86,7 +82,7 @@ func globalRunnerMiddleware(next http.Handler) http.Handler {
return
}
context.Set(r, "runner", runner)
context.Set(r, "runner", &runner)
next.ServeHTTP(w, r)
})
}
@ -94,12 +90,7 @@ func globalRunnerMiddleware(next http.Handler) http.Handler {
func getGlobalRunner(w http.ResponseWriter, r *http.Request) {
runner := context.Get(r, "runner").(*db.Runner)
helpers.WriteJSON(w, http.StatusOK, minimalGlobalRunner{
Name: "",
Active: true,
Webhook: runner.Webhook,
MaxParallelTasks: runner.MaxParallelTasks,
})
helpers.WriteJSON(w, http.StatusOK, runner)
}
func updateGlobalRunner(w http.ResponseWriter, r *http.Request) {
@ -150,7 +141,7 @@ func setGlobalRunnerActive(w http.ResponseWriter, r *http.Request) {
runner.Active = body.Active
err := store.UpdateRunner(runner)
err := store.UpdateRunner(*runner)
if err != nil {
helpers.WriteErrorStatus(w, err.Error(), http.StatusBadRequest)
@ -159,40 +150,3 @@ func setGlobalRunnerActive(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
//func updateUser(w http.ResponseWriter, r *http.Request) {
// targetUser := context.Get(r, "_user").(db.User)
// editor := context.Get(r, "user").(*db.User)
//
// var user db.UserWithPwd
// if !helpers.Bind(w, r, &user) {
// return
// }
//
// if !editor.Admin && editor.ID != targetUser.ID {
// log.Warn(editor.Username + " is not permitted to edit users")
// w.WriteHeader(http.StatusUnauthorized)
// return
// }
//
// if editor.ID == targetUser.ID && targetUser.Admin != user.Admin {
// log.Warn("User can't edit his own role")
// w.WriteHeader(http.StatusUnauthorized)
// return
// }
//
// if targetUser.External && targetUser.Username != user.Username {
// log.Warn("Username is not editable for external users")
// w.WriteHeader(http.StatusBadRequest)
// return
// }
//
// user.ID = targetUser.ID
// if err := helpers.Store(r).UpdateUser(user); err != nil {
// log.Error(err.Error())
// w.WriteHeader(http.StatusBadRequest)
// return
// }
//
// w.WriteHeader(http.StatusNoContent)
//}

View File

@ -14,7 +14,7 @@ import (
func RunnerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-API-Token")
token := r.Header.Get("X-Runner-Token")
if token == "" {
helpers.WriteJSON(w, http.StatusUnauthorized, map[string]string{

View File

@ -8,6 +8,6 @@
"runner": {
"config_file": "/tmp/semaphore-runner.json",
"api_url": "http://localhost:3000/api"
"api_url": "http://localhost:3000/internal"
}
}

View File

@ -257,7 +257,7 @@ type Store interface {
GetRunners(projectID int) ([]Runner, error)
DeleteRunner(projectID int, runnerID int) error
GetGlobalRunner(runnerID int) (Runner, error)
GetGlobalRunners() ([]Runner, error)
GetGlobalRunners(activeOnly bool) ([]Runner, error)
DeleteGlobalRunner(runnerID int) error
UpdateRunner(runner Runner) error
CreateRunner(runner Runner) (Runner, error)

View File

@ -23,8 +23,14 @@ func (d *BoltDb) GetGlobalRunner(runnerID int) (runner db.Runner, err error) {
return
}
func (d *BoltDb) GetGlobalRunners() (runners []db.Runner, err error) {
err = d.getObjects(0, db.GlobalRunnerProps, db.RetrieveQueryParams{}, nil, &runners)
func (d *BoltDb) GetGlobalRunners(activeOnly bool) (runners []db.Runner, err error) {
err = d.getObjects(0, db.GlobalRunnerProps, db.RetrieveQueryParams{}, func(i interface{}) bool {
runner := i.(*db.Runner)
if activeOnly {
return runner.Active
}
return true
}, &runners)
return
}

View File

@ -2,6 +2,7 @@ package sql
import (
"encoding/base64"
"github.com/Masterminds/squirrel"
"github.com/ansible-semaphore/semaphore/db"
"github.com/gorilla/securecookie"
)
@ -23,8 +24,14 @@ func (d *SqlDb) GetGlobalRunner(runnerID int) (runner db.Runner, err error) {
return
}
func (d *SqlDb) GetGlobalRunners() (runners []db.Runner, err error) {
err = d.getObjects(0, db.GlobalRunnerProps, db.RetrieveQueryParams{}, nil, &runners)
func (d *SqlDb) GetGlobalRunners(activeOnly bool) (runners []db.Runner, err error) {
err = d.getObjects(0, db.GlobalRunnerProps, db.RetrieveQueryParams{}, func(builder squirrel.SelectBuilder) squirrel.SelectBuilder {
if activeOnly {
builder = builder.Where("active=?", activeOnly)
}
return builder
}, &runners)
return
}

View File

@ -5,7 +5,7 @@ services:
image: docker.io/semaphoreui/runner:${SEMAPHORE_VERSION:-latest}
restart: always
environment:
SEMAPHORE_RUNNER_API_URL: ${SEMAPHORE_RUNNER_API_URL:-http://server:3000/api}
SEMAPHORE_RUNNER_API_URL: ${SEMAPHORE_RUNNER_API_URL:-http://server:3000/internal}
SEMAPHORE_RUNNER_REGISTRATION_TOKEN: ${SEMAPHORE_RUNNER_REGISTRATION_TOKEN:-H1wDyorbg6gTSwJlVwle2Fne}
server:

View File

@ -220,7 +220,7 @@ func (p *JobPool) sendProgress() {
return
}
req.Header.Set("X-API-Token", p.config.Token)
req.Header.Set("X-Runner-Token", p.config.Token)
resp, err := client.Do(req)
if err != nil {
@ -315,7 +315,7 @@ func (p *JobPool) checkNewJobs() {
req, err := http.NewRequest("GET", url, nil)
req.Header.Set("X-API-Token", p.config.Token)
req.Header.Set("X-Runner-Token", p.config.Token)
if err != nil {
fmt.Println("Error creating request:", err)

View File

@ -79,7 +79,7 @@ func (t *RemoteJob) Run(username string, incomingVersion *string) (err error) {
var runners []db.Runner
db.StoreSession(t.taskPool.store, "run remote job", func() {
runners, err = t.taskPool.store.GetGlobalRunners()
runners, err = t.taskPool.store.GetGlobalRunners(true)
})
if err != nil {

View File

@ -26,7 +26,7 @@
@yes="deleteItem(itemId)"
/>
<v-toolbar flat >
<v-toolbar flat>
<v-btn
icon
class="mr-4"
@ -39,7 +39,8 @@
<v-btn
color="primary"
@click="editItem('new')"
>{{ $t('newRunner') }}</v-btn>
>{{ $t('newRunner') }}
</v-btn>
</v-toolbar>
<v-data-table
@ -48,20 +49,17 @@
class="mt-4"
:footer-props="{ itemsPerPageOptions: [20] }"
>
<template v-slot:item.external="{ item }">
<v-icon v-if="item.external">mdi-checkbox-marked</v-icon>
<v-icon v-else>mdi-checkbox-blank-outline</v-icon>
<template v-slot:item.active="{ item }">
<v-switch
v-model="item.active"
inset
@change="setActive(item.id, item.active)"
></v-switch>
</template>
<template v-slot:item.alert="{ item }">
<v-icon v-if="item.alert">mdi-checkbox-marked</v-icon>
<v-icon v-else>mdi-checkbox-blank-outline</v-icon>
</template>
<template v-slot:item.name="{ item }">{{ item.name || '&mdash;' }}</template>
<template v-slot:item.admin="{ item }">
<v-icon v-if="item.admin">mdi-checkbox-marked</v-icon>
<v-icon v-else>mdi-checkbox-blank-outline</v-icon>
</template>
<template v-slot:item.webhook="{ item }">{{ item.webhook || '&mdash;' }}</template>
<template v-slot:item.actions="{ item }">
<div style="white-space: nowrap">
@ -92,6 +90,7 @@ import YesNoDialog from '@/components/YesNoDialog.vue';
import ItemListPageBase from '@/components/ItemListPageBase';
import EditDialog from '@/components/EditDialog.vue';
import RunnerForm from '@/components/RunnerForm.vue';
import axios from 'axios';
export default {
mixins: [ItemListPageBase],
@ -103,16 +102,38 @@ export default {
},
methods: {
async setActive(runnerId, active) {
await axios({
method: 'post',
url: `/api/runners/${runnerId}/active`,
responseType: 'json',
data: {
active,
},
});
},
getHeaders() {
return [{
text: this.$i18n.t('name'),
value: 'name',
width: '50%',
},
{
text: this.$i18n.t('active'),
value: 'active',
}];
return [
{
value: 'active',
}, {
text: this.$i18n.t('name'),
value: 'name',
width: '50%',
},
{
text: this.$i18n.t('webhook'),
value: 'webhook',
},
{
text: this.$i18n.t('max_parallel_tasks'),
value: 'max_parallel_tasks',
}, {
text: this.$i18n.t('actions'),
value: 'actions',
sortable: false,
}];
},
async returnToProjects() {