-
Notifications
You must be signed in to change notification settings - Fork 54
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
Adding osctrl-api component #28
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/jinzhu/gorm" | ||
_ "github.com/jinzhu/gorm/dialects/postgres" | ||
"github.com/jmpsec/osctrl/pkg/types" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
// Function to load the DB configuration file and assign to variables | ||
func loadDBConfiguration(file string) (types.JSONConfigurationDB, error) { | ||
var config types.JSONConfigurationDB | ||
log.Printf("Loading %s", file) | ||
// Load file and read config | ||
viper.SetConfigFile(file) | ||
err := viper.ReadInConfig() | ||
if err != nil { | ||
return config, err | ||
} | ||
// Backend values | ||
dbRaw := viper.Sub("db") | ||
err = dbRaw.Unmarshal(&config) | ||
if err != nil { | ||
return config, err | ||
} | ||
// No errors! | ||
return config, nil | ||
} | ||
|
||
// Get PostgreSQL DB using GORM | ||
func getDB(file string) *gorm.DB { | ||
// Load DB configuration | ||
dbConfig, err := loadDBConfiguration(file) | ||
if err != nil { | ||
log.Fatalf("Error loading DB configuration %v", err) | ||
} | ||
t := "host=%s port=%s dbname=%s user=%s password=%s sslmode=disable" | ||
postgresDSN := fmt.Sprintf( | ||
t, dbConfig.Host, dbConfig.Port, dbConfig.Name, dbConfig.Username, dbConfig.Password) | ||
db, err := gorm.Open("postgres", postgresDSN) | ||
if err != nil { | ||
log.Fatalf("Failed to open database connection: %v", err) | ||
} | ||
// Performance settings for DB access | ||
db.DB().SetMaxIdleConns(dbConfig.MaxIdleConns) | ||
db.DB().SetMaxOpenConns(dbConfig.MaxOpenConns) | ||
db.DB().SetConnMaxLifetime(time.Second * time.Duration(dbConfig.ConnMaxLifetime)) | ||
|
||
return db | ||
} | ||
|
||
// Automigrate of tables | ||
func automigrateDB() error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this supposed to do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
var err error | ||
return err | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"net/http" | ||
|
||
"github.com/gorilla/mux" | ||
"github.com/jmpsec/osctrl/pkg/settings" | ||
"github.com/jmpsec/osctrl/pkg/utils" | ||
) | ||
|
||
// GET Handler for single JSON environment | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean this API is used to validate if an environment exists in the backend? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, and returns all data for that environment. Does it make more sense to have a verb to check on a specific environment, passing the name, and return just a boolean? |
||
func apiEnvironmentHandler(w http.ResponseWriter, r *http.Request) { | ||
incMetric(metricAPIReq) | ||
utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) | ||
vars := mux.Vars(r) | ||
// Extract name | ||
name, ok := vars["name"] | ||
if !ok { | ||
incMetric(metricAPIErr) | ||
apiErrorResponse(w, "error getting name", nil) | ||
return | ||
} | ||
// Get environment by name | ||
env, err := envs.Get(name) | ||
if err != nil { | ||
incMetric(metricAPIErr) | ||
if err.Error() == "record not found" { | ||
log.Printf("environment not found: %s", name) | ||
apiHTTPResponse(w, JSONApplicationUTF8, http.StatusNotFound, ApiErrorResponse{Error: "environment not found"}) | ||
} else { | ||
apiErrorResponse(w, "error getting environment", err) | ||
} | ||
return | ||
} | ||
// Header to serve JSON | ||
apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, env) | ||
incMetric(metricAPIOK) | ||
} | ||
|
||
// GET Handler for multiple JSON environments | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And this returns all environments? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yessir. |
||
func apiEnvironmentsHandler(w http.ResponseWriter, r *http.Request) { | ||
incMetric(metricAPIReq) | ||
utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) | ||
// Get platforms | ||
envAll, err := envs.All() | ||
if err != nil { | ||
incMetric(metricAPIErr) | ||
apiErrorResponse(w, "error getting environments", err) | ||
return | ||
} | ||
// Header to serve JSON | ||
apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, envAll) | ||
incMetric(metricAPIOK) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"net/http" | ||
|
||
"github.com/gorilla/mux" | ||
"github.com/jmpsec/osctrl/pkg/settings" | ||
"github.com/jmpsec/osctrl/pkg/utils" | ||
) | ||
|
||
// GET Handler for single JSON nodes | ||
func apiNodeHandler(w http.ResponseWriter, r *http.Request) { | ||
incMetric(metricAPIReq) | ||
utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) | ||
vars := mux.Vars(r) | ||
// Extract uuid | ||
uuid, ok := vars["uuid"] | ||
if !ok { | ||
incMetric(metricAPIErr) | ||
apiErrorResponse(w, "error getting uuid", nil) | ||
return | ||
} | ||
// Get node by UUID | ||
node, err := nodesmgr.GetByUUID(uuid) | ||
if err != nil { | ||
incMetric(metricAPIErr) | ||
if err.Error() == "record not found" { | ||
log.Printf("node not found: %s", uuid) | ||
apiHTTPResponse(w, JSONApplicationUTF8, http.StatusNotFound, ApiErrorResponse{Error: "node not found"}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is what this returns a similar JSON format to successful run? If so that'll make it easier to parse in prospective clients. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the requested node exists, it does return the structure for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No that should be fine, a client can easily check that the fields are empty :) |
||
} else { | ||
apiErrorResponse(w, "error getting node", err) | ||
} | ||
return | ||
} | ||
// Serialize and serve JSON | ||
apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, node) | ||
incMetric(metricAPIOK) | ||
} | ||
|
||
// GET Handler for multiple JSON nodes | ||
func apiNodesHandler(w http.ResponseWriter, r *http.Request) { | ||
incMetric(metricAPIReq) | ||
utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) | ||
// Get nodes | ||
nodes, err := nodesmgr.Gets("all", 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems an odd API to use over a more easily locked down Any reason why you're doing it this way? |
||
if err != nil { | ||
incMetric(metricAPIErr) | ||
apiErrorResponse(w, "error getting nodes", err) | ||
return | ||
} | ||
if len(nodes) == 0 { | ||
incMetric(metricAPIErr) | ||
log.Printf("no nodes") | ||
apiHTTPResponse(w, JSONApplicationUTF8, http.StatusNotFound, ApiErrorResponse{Error: "no nodes"}) | ||
return | ||
} | ||
// Serialize and serve JSON | ||
apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, nodes) | ||
incMetric(metricAPIOK) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package main | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/jmpsec/osctrl/pkg/settings" | ||
"github.com/jmpsec/osctrl/pkg/utils" | ||
) | ||
|
||
// GET Handler for multiple JSON platforms | ||
func apiPlatformsHandler(w http.ResponseWriter, r *http.Request) { | ||
incMetric(metricAPIReq) | ||
utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) | ||
// Get platforms | ||
platforms, err := nodesmgr.GetAllPlatforms() | ||
if err != nil { | ||
incMetric(metricAPIErr) | ||
apiErrorResponse(w, "error getting platforms", err) | ||
return | ||
} | ||
// Serialize and serve JSON | ||
apiHTTPResponse(w, JSONApplicationUTF8, http.StatusOK, platforms) | ||
incMetric(metricAPIOK) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is hardcoding this ok or should we use hostnames (or configurable values)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the IP address for the vagrant machine. There is no way (that I know of) to configure this via a command and is better to have the IP set to something known. Since the flow is to run
vagrant up
this should be fine.