Skip to content

Commit 55ab5fe

Browse files
author
Nicolas Sterchele
committed
Implement api as rundeck option provider
- Use rundeck's name value list format https://docs.rundeck.com/docs/manual/job-options.html#option-model-provider - Use selected param as query string to select by default the returns values - Multi groups selection separated by comma - Return 200 response code for empty host ref https://github.com/peopledoc/peopleinfra/issues/8328
1 parent 97ee30c commit 55ab5fe

25 files changed

+574
-84
lines changed

api/handlers/check.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package handlers
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
7+
log "github.com/sirupsen/logrus"
8+
)
9+
10+
type checkHandler struct {
11+
*log.Logger
12+
}
13+
14+
func (cH checkHandler) check(w http.ResponseWriter, r *http.Request) {
15+
log.Trace("health path called")
16+
okResp := map[string]bool{"ok": true}
17+
18+
js, err := json.Marshal(okResp)
19+
if err != nil {
20+
http.Error(w, err.Error(), http.StatusInternalServerError)
21+
return
22+
}
23+
24+
w.Header().Set("Content-Type", "application/json")
25+
w.Write(js)
26+
return
27+
}

api/handlers/groups.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package handlers
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"jarvis/internal/pkg/ansible"
7+
"jarvis/internal/pkg/environment"
8+
"net/http"
9+
"path"
10+
11+
"github.com/gorilla/mux"
12+
log "github.com/sirupsen/logrus"
13+
"github.com/spf13/viper"
14+
)
15+
16+
type groupsHandler struct {
17+
*log.Logger
18+
}
19+
20+
func (gH groupsHandler) list(w http.ResponseWriter, r *http.Request) {
21+
log.Trace("hosts path called")
22+
vars := mux.Vars(r)
23+
helperPath := path.Join(
24+
viper.GetString("environments.path"), viper.GetString("environments.helper"))
25+
26+
//env, group
27+
if rawEnv, ok := vars["env"]; ok {
28+
env, err := environment.ParseRawEnvironmentPredicate(helperPath, rawEnv)
29+
if err != nil {
30+
logErrorToResponse(err, "groups", w)
31+
return
32+
}
33+
envs := []*environment.Environment{env}
34+
invReaders, err := inventoriesReaders(envs)
35+
if err != nil {
36+
logErrorToResponse(err, "groups", w)
37+
return
38+
}
39+
r := io.MultiReader(invReaders...)
40+
manipulator, err := ansible.InitInventoryManipulator(r)
41+
if err != nil {
42+
logErrorToResponse(err, "groups", w)
43+
return
44+
}
45+
w.Header().Set("Content-Type", "application/json")
46+
47+
groups, err := manipulator.GetGroupsName(false)
48+
if err != nil {
49+
logErrorToResponse(err, "groups", w)
50+
return
51+
}
52+
53+
js, err := json.Marshal(groups)
54+
if err != nil {
55+
logErrorToResponse(err, "groups", w)
56+
return
57+
}
58+
59+
w.Write(js)
60+
return
61+
}
62+
}

api/handlers/handlers.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package handlers
2+
3+
import (
4+
"io"
5+
"jarvis/internal/pkg/ansible"
6+
"jarvis/internal/pkg/environment"
7+
"net/http"
8+
9+
"github.com/gorilla/mux"
10+
log "github.com/sirupsen/logrus"
11+
"github.com/spf13/viper"
12+
)
13+
14+
func API(log *log.Logger) http.Handler {
15+
router := mux.NewRouter().StrictSlash(true)
16+
17+
//check (health)
18+
cH := checkHandler{log}
19+
router.HandleFunc("/health", cH.check).
20+
Methods("GET")
21+
22+
//playbooks
23+
pH := playbookHandler{log}
24+
router.HandleFunc("/playbooks", pH.list).
25+
Methods("GET")
26+
27+
//platforms
28+
ptH := platformsHandler{log}
29+
router.HandleFunc("/platforms/{env}", ptH.list).
30+
Methods("GET")
31+
32+
//groups
33+
gH := groupsHandler{log}
34+
router.HandleFunc("/groups/{env}", gH.list).
35+
Methods("GET")
36+
37+
//hosts
38+
hH := hostsHandler{log}
39+
router.HandleFunc("/hosts/{env}/", func(w http.ResponseWriter, r *http.Request) {
40+
w.WriteHeader(http.StatusNoContent)
41+
}).
42+
Methods("GET")
43+
router.HandleFunc("/hosts/{env}/{groups}", hH.list).
44+
Methods("GET")
45+
46+
return router
47+
}
48+
49+
func logErrorToResponse(err error, handler string, w http.ResponseWriter) {
50+
http.Error(w, err.Error(), http.StatusInternalServerError)
51+
log.WithFields(log.Fields{
52+
"handler": handler,
53+
}).Error(err.Error())
54+
}
55+
56+
func inventoriesReaders(envs []*environment.Environment) ([]io.Reader, error) {
57+
envsPath := viper.GetString("environments.path")
58+
59+
allInventories, err := environment.GetFullPathInventoriesFromEnvironments(envsPath, envs)
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
invReaders, err := ansible.BuildReadersFromInventoriesPath(allInventories)
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
return invReaders, nil
70+
}

api/handlers/hosts.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package handlers
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"jarvis/internal/pkg/ansible"
7+
"jarvis/internal/pkg/environment"
8+
"net/http"
9+
"path"
10+
"strconv"
11+
"strings"
12+
13+
"github.com/gorilla/mux"
14+
log "github.com/sirupsen/logrus"
15+
"github.com/spf13/viper"
16+
)
17+
18+
type hostsHandler struct {
19+
*log.Logger
20+
}
21+
22+
func (hH hostsHandler) list(w http.ResponseWriter, r *http.Request) {
23+
log.Trace("hosts path called")
24+
vars := mux.Vars(r)
25+
helperPath := path.Join(
26+
viper.GetString("environments.path"), viper.GetString("environments.helper"))
27+
28+
//lets return selected at true by default
29+
selectedRaw, selectedInQuery := r.URL.Query()["selected"]
30+
selected := true
31+
if selectedInQuery && len(selectedRaw) > 0 {
32+
//don't care of multiple `selected` query parameter
33+
if s, err := strconv.ParseBool(selectedRaw[0]); err == nil {
34+
selected = s
35+
}
36+
}
37+
38+
//env, group
39+
if rawEnv, ok := vars["env"]; ok {
40+
env, err := environment.ParseRawEnvironmentPredicate(helperPath, rawEnv)
41+
if err != nil {
42+
logErrorToResponse(err, "hosts", w)
43+
return
44+
}
45+
envs := []*environment.Environment{env}
46+
invReaders, err := inventoriesReaders(envs)
47+
if err != nil {
48+
logErrorToResponse(err, "hosts", w)
49+
return
50+
}
51+
r := io.MultiReader(invReaders...)
52+
manipulator, err := ansible.InitInventoryManipulator(r)
53+
if err != nil {
54+
logErrorToResponse(err, "hosts", w)
55+
return
56+
}
57+
w.Header().Set("Content-Type", "application/json")
58+
59+
if rawGroups, ok := vars["groups"]; ok {
60+
groups := strings.Split(rawGroups, ",")
61+
var hosts []string
62+
for _, group := range groups {
63+
h, err := manipulator.GetHostsByGroupName(group)
64+
hosts = append(hosts, h...)
65+
if err != nil {
66+
logErrorToResponse(err, "hosts", w)
67+
return
68+
}
69+
}
70+
js, err := json.Marshal(fillRundeckResult(hosts, selected))
71+
if err != nil {
72+
logErrorToResponse(err, "hosts", w)
73+
return
74+
}
75+
76+
w.Write(js)
77+
return
78+
}
79+
}
80+
}

api/handlers/platforms.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package handlers
2+
3+
import (
4+
"jarvis/internal/pkg/environment"
5+
"net/http"
6+
"path"
7+
8+
"github.com/gorilla/mux"
9+
log "github.com/sirupsen/logrus"
10+
"github.com/spf13/viper"
11+
)
12+
13+
type platformsHandler struct {
14+
*log.Logger
15+
}
16+
17+
func (pH platformsHandler) list(w http.ResponseWriter, r *http.Request) {
18+
log.Trace("platforms path called")
19+
helperPath := path.Join(
20+
viper.GetString("environments.path"), viper.GetString("environments.helper"))
21+
vars := mux.Vars(r)
22+
23+
env, err := environment.ParseRawEnvironmentPredicate(helperPath, vars["env"])
24+
if err != nil {
25+
logErrorToResponse(err, "platforms", w)
26+
return
27+
}
28+
29+
w.Header().Set("Content-Type", "application/json")
30+
31+
printer := environment.JsonPrinter{}
32+
printer.PrintEnvironments(w, []*environment.Environment{env})
33+
}

api/handlers/playbooks.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package handlers
2+
3+
import (
4+
"encoding/json"
5+
"jarvis/internal/pkg/ansible"
6+
"net/http"
7+
8+
"github.com/spf13/viper"
9+
10+
log "github.com/sirupsen/logrus"
11+
)
12+
13+
type playbookHandler struct {
14+
*log.Logger
15+
}
16+
17+
func (pH playbookHandler) list(w http.ResponseWriter, r *http.Request) {
18+
pH.Trace("playbooks path called")
19+
pBookPath := viper.GetString("ansible.playbook.playbooks_path")
20+
pbooks, err := ansible.ListPlaybooks(pBookPath)
21+
if err != nil {
22+
logErrorToResponse(err, "playbook", w)
23+
return
24+
}
25+
js, err := json.Marshal(pbooks)
26+
if err != nil {
27+
http.Error(w, err.Error(), http.StatusInternalServerError)
28+
return
29+
}
30+
31+
w.Header().Set("Content-Type", "application/json")
32+
w.Write(js)
33+
}

api/handlers/rundeck.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package handlers
2+
3+
type rundeckResult struct {
4+
Name string `json:"name"`
5+
Value string `json:"value"`
6+
Selected bool `json:"selected"`
7+
}
8+
9+
func fillRundeckResult(results []string, selected bool) []rundeckResult {
10+
var rundeckR []rundeckResult
11+
rundeckR = make([]rundeckResult, len(results), len(results))
12+
for i, r := range results {
13+
rundeckR[i] = rundeckResult{r, r, selected}
14+
}
15+
16+
return rundeckR
17+
}

api/main.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package api
2+
3+
import (
4+
"fmt"
5+
"jarvis/api/handlers"
6+
"net/http"
7+
"time"
8+
9+
log "github.com/sirupsen/logrus"
10+
)
11+
12+
type Api struct {
13+
logger *log.Logger
14+
port int
15+
}
16+
17+
func InitApi(logger *log.Logger, port int) *Api {
18+
return &Api{logger, port}
19+
}
20+
21+
func (api *Api) Run() error {
22+
var httpSrv *http.Server
23+
24+
h := handlers.API(api.logger)
25+
httpSrv = &http.Server{
26+
ReadTimeout: 5 * time.Second,
27+
WriteTimeout: 5 * time.Second,
28+
Handler: h,
29+
Addr: fmt.Sprintf(":%d", api.port),
30+
}
31+
32+
err := httpSrv.ListenAndServe()
33+
34+
return err
35+
}

0 commit comments

Comments
 (0)