Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runners ui #2377

Merged
merged 19 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ web/public/fonts/*.*
web/.nyc_output
api/public/**/*
/config.json
/config-runner.json
/config-runner-token.txt
/.dredd/config.json
/database.boltdb
/database.boltdb.lock
Expand All @@ -29,3 +31,4 @@ node_modules/
.vscode
__debug_bin*
.task/
/web/.env
1 change: 1 addition & 0 deletions api/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ func setAppActive(w http.ResponseWriter, r *http.Request) {
}

if !helpers.Bind(w, r, &body) {
helpers.WriteErrorStatus(w, "Invalid request body", http.StatusBadRequest)
return
}

Expand Down
30 changes: 22 additions & 8 deletions api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,20 @@ func Route() *mux.Router {
publicAPIRouter := r.PathPrefix(webPath + "api").Subrouter()
publicAPIRouter.Use(StoreMiddleware, JSONMiddleware)

publicAPIRouter.HandleFunc("/runners", runners.RegisterRunner).Methods("POST")
publicAPIRouter.HandleFunc("/auth/login", login).Methods("GET", "POST")
publicAPIRouter.HandleFunc("/auth/logout", logout).Methods("POST")
publicAPIRouter.HandleFunc("/auth/oidc/{provider}/login", oidcLogin).Methods("GET")
publicAPIRouter.HandleFunc("/auth/oidc/{provider}/redirect", oidcRedirect).Methods("GET")
publicAPIRouter.HandleFunc("/auth/oidc/{provider}/redirect/{redirect_path:.*}", oidcRedirect).Methods("GET")

routersAPI := r.PathPrefix(webPath + "api").Subrouter()
routersAPI.Use(StoreMiddleware, JSONMiddleware, runners.RunnerMiddleware)
routersAPI.Path("/runners/{runner_id}").HandlerFunc(runners.GetRunner).Methods("GET", "HEAD")
routersAPI.Path("/runners/{runner_id}").HandlerFunc(runners.UpdateRunner).Methods("PUT")
routersAPI.Path("/runners/{runner_id}").HandlerFunc(runners.UnregisterRunner).Methods("DELETE")
internalAPI := publicAPIRouter.PathPrefix("/internal").Subrouter()
internalAPI.HandleFunc("/runners", runners.RegisterRunner).Methods("POST")

runnersAPI := internalAPI.PathPrefix("/runners").Subrouter()
runnersAPI.Use(runners.RunnerMiddleware)
runnersAPI.Path("").HandlerFunc(runners.GetRunner).Methods("GET", "HEAD")
runnersAPI.Path("").HandlerFunc(runners.UpdateRunner).Methods("PUT")
runnersAPI.Path("").HandlerFunc(runners.UnregisterRunner).Methods("DELETE")

publicWebHookRouter := r.PathPrefix(webPath + "api").Subrouter()
publicWebHookRouter.Use(StoreMiddleware, JSONMiddleware)
Expand Down Expand Up @@ -128,6 +130,16 @@ func Route() *mux.Router {
adminAPI.Path("/options").HandlerFunc(getOptions).Methods("GET", "HEAD")
adminAPI.Path("/options").HandlerFunc(setOption).Methods("POST")

adminAPI.Path("/runners").HandlerFunc(getGlobalRunners).Methods("GET", "HEAD")
adminAPI.Path("/runners").HandlerFunc(addGlobalRunner).Methods("POST", "HEAD")

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")
globalRunnersAPI.Path("/{runner_id}/active").HandlerFunc(setGlobalRunnerActive).Methods("POST")
globalRunnersAPI.Path("/{runner_id}").HandlerFunc(deleteGlobalRunner).Methods("DELETE")

appsAPI := adminAPI.PathPrefix("/apps").Subrouter()
appsAPI.Use(appMiddleware)
appsAPI.Path("/{app_id}").HandlerFunc(getApp).Methods("GET", "HEAD")
Expand Down Expand Up @@ -446,8 +458,10 @@ func serveFile(w http.ResponseWriter, r *http.Request, name string) {

func getSystemInfo(w http.ResponseWriter, r *http.Request) {
body := map[string]interface{}{
"version": util.Version(),
"ansible": util.AnsibleVersion(),
"version": util.Version(),
"ansible": util.AnsibleVersion(),
"web_host": util.WebHostURL.String(),
"use_remote_runner": util.Config.UseRemoteRunner,
}

helpers.WriteJSON(w, http.StatusOK, body)
Expand Down
157 changes: 157 additions & 0 deletions api/runners.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package api

import (
"github.com/ansible-semaphore/semaphore/api/helpers"
"github.com/ansible-semaphore/semaphore/db"
log "github.com/sirupsen/logrus"
"net/http"

"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"`
//}

func getGlobalRunners(w http.ResponseWriter, r *http.Request) {
runners, err := helpers.Store(r).GetGlobalRunners(false)

if err != nil {
panic(err)
}

var result = make([]db.Runner, 0)

for _, runner := range runners {
result = append(result, runner)
}

helpers.WriteJSON(w, http.StatusOK, result)
}

type runnerWithToken struct {
db.Runner
Token string `json:"token"`
}

func addGlobalRunner(w http.ResponseWriter, r *http.Request) {
var runner db.Runner
if !helpers.Bind(w, r, &runner) {
return
}

runner.ProjectID = nil
newRunner, err := helpers.Store(r).CreateRunner(runner)

if err != nil {
log.Warn("Runner is not created: " + err.Error())
w.WriteHeader(http.StatusBadRequest)
return
}

helpers.WriteJSON(w, http.StatusCreated, runnerWithToken{
Runner: newRunner,
Token: newRunner.Token,
})
}

func globalRunnerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
runnerID, err := helpers.GetIntParam("runner_id", w, r)

if err != nil {
helpers.WriteJSON(w, http.StatusBadRequest, map[string]string{
"error": "runner_id required",
})
return
}

store := helpers.Store(r)

runner, err := store.GetGlobalRunner(runnerID)

if err != nil {
helpers.WriteJSON(w, http.StatusNotFound, map[string]string{
"error": "Runner not found",
})
return
}

context.Set(r, "runner", &runner)
next.ServeHTTP(w, r)
})
}

func getGlobalRunner(w http.ResponseWriter, r *http.Request) {
runner := context.Get(r, "runner").(*db.Runner)

helpers.WriteJSON(w, http.StatusOK, runner)
}

func updateGlobalRunner(w http.ResponseWriter, r *http.Request) {
oldRunner := context.Get(r, "runner").(*db.Runner)

var runner db.Runner
if !helpers.Bind(w, r, &runner) {
return
}

store := helpers.Store(r)

runner.ID = oldRunner.ID
runner.ProjectID = nil

err := store.UpdateRunner(runner)

if err != nil {
helpers.WriteErrorStatus(w, err.Error(), http.StatusBadRequest)
return
}

w.WriteHeader(http.StatusNoContent)
}

func deleteGlobalRunner(w http.ResponseWriter, r *http.Request) {
runner := context.Get(r, "runner").(*db.Runner)

store := helpers.Store(r)

err := store.DeleteGlobalRunner(runner.ID)

if err != nil {
helpers.WriteErrorStatus(w, err.Error(), http.StatusBadRequest)
return
}

w.WriteHeader(http.StatusNoContent)
}

func setGlobalRunnerActive(w http.ResponseWriter, r *http.Request) {
runner := context.Get(r, "runner").(*db.Runner)

store := helpers.Store(r)

var body struct {
Active bool `json:"active"`
}

if !helpers.Bind(w, r, &body) {
helpers.WriteErrorStatus(w, "Invalid request body", http.StatusBadRequest)
return
}

runner.Active = body.Active

err := store.UpdateRunner(*runner)

if err != nil {
helpers.WriteErrorStatus(w, err.Error(), http.StatusBadRequest)
return
}

w.WriteHeader(http.StatusNoContent)
}
20 changes: 6 additions & 14 deletions api/runners/runners.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -23,18 +23,9 @@ func RunnerMiddleware(next http.Handler) http.Handler {
return
}

runnerID, err := helpers.GetIntParam("runner_id", w, r)

if err != nil {
helpers.WriteJSON(w, http.StatusBadRequest, map[string]string{
"error": "runner_id required",
})
return
}

store := helpers.Store(r)

runner, err := store.GetGlobalRunner(runnerID)
runner, err := store.GetGlobalRunnerByToken(token)

if err != nil {
helpers.WriteJSON(w, http.StatusNotFound, map[string]string{
Expand Down Expand Up @@ -199,11 +190,12 @@ func RegisterRunner(w http.ResponseWriter, r *http.Request) {
return
}

res := util.RunnerConfig{
RunnerID: runner.ID,
Token: runner.Token,
var res struct {
Token string `json:"token"`
}

res.Token = runner.Token

helpers.WriteJSON(w, http.StatusOK, res)
}

Expand Down
6 changes: 4 additions & 2 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
)

var configPath string
var noConfig bool

var rootCmd = &cobra.Command{
Use: "semaphore",
Expand All @@ -34,8 +35,9 @@ Complete documentation is available at https://ansible-semaphore.com.`,

func Execute() {
rootCmd.PersistentFlags().StringVar(&configPath, "config", "", "Configuration file path")
rootCmd.PersistentFlags().BoolVar(&noConfig, "no-config", false, "Don't use configuration file")
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
Expand Down Expand Up @@ -96,7 +98,7 @@ func runService() {
}

func createStore(token string) db.Store {
util.ConfigInit(configPath)
util.ConfigInit(configPath, noConfig)

store := factory.CreateStore()

Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/runner_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func init() {
}

func runRunner() {
util.ConfigInit(configPath)
util.ConfigInit(configPath, noConfig)

taskPool := runners.JobPool{}

Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/runner_unregister.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func init() {
}

func unregisterRunner() {
util.ConfigInit(configPath)
util.ConfigInit(configPath, noConfig)

taskPool := runners.JobPool{}
err := taskPool.Unregister()
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func doSetup() int {

configPath := setup.SaveConfig(config)

util.ConfigInit(configPath)
util.ConfigInit(configPath, false)

fmt.Println(" Pinging db..")

Expand Down
13 changes: 0 additions & 13 deletions config-runner.json

This file was deleted.

1 change: 1 addition & 0 deletions db/Migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func GetMigrations() []Migration {
{Version: "2.10.12"},
{Version: "2.10.15"},
{Version: "2.10.16"},
{Version: "2.10.26"},
}
}

Expand Down
4 changes: 3 additions & 1 deletion db/Runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ type RunnerState string
//)

type Runner struct {
ID int `db:"id" json:"-"`
ID int `db:"id" json:"id"`
Token string `db:"token" json:"-"`
ProjectID *int `db:"project_id" json:"project_id"`
//State RunnerState `db:"state" json:"state"`
Webhook string `db:"webhook" json:"webhook"`
MaxParallelTasks int `db:"max_parallel_tasks" json:"max_parallel_tasks"`
Active bool `db:"active" json:"active"`
Name string `db:"name" json:"name"`
}
3 changes: 2 additions & 1 deletion db/Store.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,9 @@ type Store interface {
GetRunner(projectID int, runnerID int) (Runner, error)
GetRunners(projectID int) ([]Runner, error)
DeleteRunner(projectID int, runnerID int) error
GetGlobalRunnerByToken(token string) (Runner, 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)
Expand Down
Loading