Commit 480ea7ad authored by Profpatsch's avatar Profpatsch

tools/hydra-to-docker-repo: init, basic hydra API querying

parent bd04f830
// Package hydra interfaces with the hydra JSON API.
package hydra
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"path"
"strconv"
"time"
)
// RETRIES hydra reqeusts returning status 500 this many times.
var RETRIES = 3
// TIMEOUTs http requests after this amount of seconds.
var TIMEOUT = 10 * time.Second
// Hydra exposes interaction with a hydra instance.
type Hydra struct {
baseUrl url.URL
client *http.Client
}
func New(baseUrl url.URL) *Hydra {
return &Hydra{
baseUrl: baseUrl,
client: &http.Client{Timeout: TIMEOUT},
}
}
// Endpoint of hydra that can be queried.
type Endpoint string
// Eval JSON output of hydra (partial).
type Eval struct {
Evals []struct {
// unique id of that eval
Id int
HasNewBuilds int
// list of build ids
Builds []int
}
}
// Build JSON output of hydra (partial).
type Build struct {
// nix derivation `outputs` field, keys "out", "man" …
BuildOutputs map[string]struct {
// nix storepath of output
Path string
}
// hydra outputs of build
BuildProducts map[string]struct {
// name of build output
Name string
// nix storepath of output, can be used to associate to buildoutput
Path string
// sha256hash of output file, null if no output file
Sha256Hash string
}
// buildstatus, 0 if successful
BuildStatus int
// finished, 1 if finished
Finished int
// unique id of that build
Id int
// hydra job attribute (nix attribute in hydra attrset, e.g. "pkgs.sangha")
Job string
}
// MkEndpoint creates an Endpoint, sanity checking it.
func MkEndpoint(rawEndpoint string) (Endpoint, error) {
url, err := url.Parse(rawEndpoint)
if err != nil {
return "", err
}
if url.IsAbs() {
return "", fmt.Errorf("hydraFetch: Endpoint URL must not be absolute (%v)", rawEndpoint)
}
return Endpoint(url.Path), err
}
// fetchJson queries a hydra endpoint and retries if necessary.
func (h *Hydra) fetchJson(e Endpoint) (io.ReadCloser, error) {
url := h.baseUrl
url.Path = path.Join(h.baseUrl.Path, string(e))
req, err := http.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/json")
// Hydra is buggy like that, so we retry for a bit
retryOn500 := func(req *http.Request) (*http.Response, error) {
for i := 0; i < RETRIES; i++ {
resp, err := h.client.Do(req)
if err != nil {
return nil, err
}
switch resp.StatusCode {
case http.StatusInternalServerError:
continue
default:
return resp, nil
}
}
return nil, fmt.Errorf("hydraFetch: More than %d retries for %s", RETRIES, url.String())
}
resp, err := retryOn500(req)
if err != nil {
return nil, err
}
if resp.StatusCode == http.StatusOK {
return resp.Body, nil
}
return nil, fmt.Errorf("hydraFetch: %s returned %s", url.String(), resp.Status)
}
func (h *Hydra) FetchBuild(buildId int) (*Build, error) {
endp, _ := MkEndpoint("build/" + strconv.Itoa(buildId))
response, err := h.fetchJson(endp)
if err != nil {
return nil, err
}
defer response.Close()
var result Build
err = json.NewDecoder(response).Decode(&result)
if err != nil {
return nil, err
}
return &result, nil
}
func (h *Hydra) FetchEval(project string, jobset string) (*Eval, error) {
endp, _ := MkEndpoint(fmt.Sprintf("jobset/%s/%s/evals", project, jobset))
response, err := h.fetchJson(endp)
if err != nil {
return nil, err
}
defer response.Close()
var result Eval
err = json.NewDecoder(response).Decode(&result)
if err != nil {
return nil, err
}
return &result, nil
}
package main
import (
"fmt"
"gitlab.techcultivation.org/sangha-deployment/tools/hydra-to-docker-repo/hydra"
// "log"
"net/url"
)
func main() {
base, _ := url.Parse(`https://headcounter.org/hydra`)
h := hydra.New(*base)
build, err := h.FetchBuild(2095071)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%+v\n", build)
eval, err := h.FetchEval("techcultivation", "sangha-deployment")
if err != nil {
fmt.Println(err)
}
fmt.Printf("%+v\n", eval)
}
package storage
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
type DB struct {
conn *sql.DB
}
func Open(sqliteDb string) (*DB, error) {
conn, err := sql.Open("sqlite3", sqliteDb)
if err != nil {
return nil, err
}
db := &DB{conn: conn}
err = db.init()
if err != nil {
return nil, err
}
return db, nil
}
func (db *DB) init() error {
// TODO INIT DB
return nil
}
func (db *DB) Close() error {
return db.conn.Close()
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment