runs with basic setup for demo

This commit is contained in:
David Lawrence 2015-06-16 12:17:40 -07:00
parent 13c4d05b79
commit 801dd397ff
5 changed files with 163 additions and 12 deletions

View File

@ -1,6 +1,7 @@
package main
import (
"database/sql"
_ "expvar"
"flag"
"fmt"
@ -13,10 +14,12 @@ import (
"github.com/Sirupsen/logrus"
_ "github.com/docker/distribution/registry/auth/token"
"github.com/endophage/gotuf/signed"
_ "github.com/go-sql-driver/mysql"
"golang.org/x/net/context"
"github.com/docker/vetinari/config"
"github.com/docker/vetinari/server"
"github.com/docker/vetinari/server/version"
"github.com/docker/vetinari/signer"
)
@ -69,6 +72,12 @@ func main() {
trust = signed.NewEd25519()
}
db, err := sql.Open("mysql", "root:@/dockercondemo")
if err != nil {
logrus.Fatal("Error starting DB driver: ", err.Error())
return // not strictly needed but let's be explicit
}
ctx = context.WithValue(ctx, "versionStore", version.NewVersionDB(db))
for {
logrus.Info("[Vetinari] Starting Server")
childCtx, cancel := context.WithCancel(ctx)

View File

@ -2,11 +2,16 @@ package handlers
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/endophage/gotuf/data"
"github.com/gorilla/mux"
"golang.org/x/net/context"
"github.com/docker/vetinari/errors"
"github.com/docker/vetinari/server/version"
)
// MainHandler is the default handler for the server
@ -29,10 +34,70 @@ func MainHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) *e
// AddHandler accepts urls in the form /<imagename>/<tag>
func UpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) *errors.HTTPError {
defer r.Body.Close()
s := ctx.Value("versionStore")
store, ok := s.(*version.VersionDB)
if !ok {
return &errors.HTTPError{
HTTPStatus: http.StatusInternalServerError,
Code: 9999,
Err: fmt.Errorf("Version store not configured"),
}
}
vars := mux.Vars(r)
qdn := vars["imageName"]
tufRole := vars["tufRole"]
input, err := ioutil.ReadAll(r.Body)
if err != nil {
return &errors.HTTPError{
HTTPStatus: http.StatusBadRequest,
Code: 9999,
Err: err,
}
}
meta := &data.SignedTargets{}
err = json.Unmarshal(input, meta)
if err != nil {
return &errors.HTTPError{
HTTPStatus: http.StatusBadRequest,
Code: 9999,
Err: err,
}
}
version := meta.Signed.Version
store.UpdateCurrent(qdn, tufRole, version, input)
return nil
}
// GetHandler accepts urls in the form /<imagename>/<tuf file>.json
func GetHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) *errors.HTTPError {
s := ctx.Value("vesionStore")
store, ok := s.(*version.VersionDB)
if !ok {
return &errors.HTTPError{
HTTPStatus: http.StatusInternalServerError,
Code: 9999,
Err: fmt.Errorf("Version store not configured"),
}
}
vars := mux.Vars(r)
qdn := vars["imageName"]
tufRole := vars["tufRole"]
data, err := store.GetCurrent(qdn, tufRole)
if err != nil {
return &errors.HTTPError{
HTTPStatus: http.StatusInternalServerError,
Code: 9999,
Err: err,
}
}
if data == nil {
return &errors.HTTPError{
HTTPStatus: http.StatusNotFound,
Code: 9999,
Err: err,
}
}
return nil
}

View File

@ -97,16 +97,17 @@ func run(ctx context.Context, addr, tlsCertFile, tlsKeyFile string, trust signed
}
tlsLsnr := tls.NewListener(lsnr, tlsConfig)
ac, err := auth.GetAccessController("token", map[string]interface{}{})
if err != nil {
return err
}
var ac auth.AccessController = nil
//ac, err := auth.GetAccessController("token", map[string]interface{}{})
//if err != nil {
// return err
//}
hand := utils.RootHandlerFactory(ac, context.Background(), trust)
r := mux.NewRouter()
// TODO (endophage): use correct regexes for image and tag names
r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufFile:(root.json|targets.json|timestamp.json|snapshot.json)}").Handler(hand(handlers.GetHandler, "pull"))
r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/{tufFile:(root.json|targets.json|timestamp.json|snapshot.json)}").Handler(hand(handlers.UpdateHandler, "push", "pull"))
r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull"))
r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.UpdateHandler, "push", "pull"))
svr := NewHTTPServer(
http.Server{

View File

@ -0,0 +1,75 @@
// version implementes a versioned store for TUF metadata
package version
import (
"database/sql"
"fmt"
)
// VersionDB implements a versioned store using a relational database.
// The database table must look like:
// CREATE TABLE `tuf_files` (
// `id` INT AUTO_INCREMENT,
// `qdn` VARCHAR(255) NOT NULL
// `role` VARCHAR(255) NOT NULL
// `version` INT
// `data` LONGBLOB
// PRIMARY KEY (`id`)
// UNIQUE INDEX (`qdn`, `role`, `version`)
// ) DEFAULT CHARSET=utf8;
type VersionDB struct {
sql.DB
}
func NewVersionDB(db *sql.DB) *VersionDB {
return &VersionDB{
DB: *db,
}
}
// Update multiple TUF records in a single transaction.
// Always insert a new row. The unique constraint will ensure there is only ever
func (vdb VersionDB) UpdateCurrent(qdn, role string, version int, data []byte) error {
checkStmt := "SELECT 1 FROM `tuf_files` WHERE `qdn`=? AND `role`=? AND `version`=?;"
insertStmt := "INSERT INTO `tuf_files` (`qdn`, `role`, `version`, `data`) VALUES (?,?,?,?) ;"
// ensure immediately previous version exists
row := vdb.QueryRow(checkStmt, qdn, role, version-1)
var exists bool
err := row.Scan(&exists)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("Attempting to increment version by more than 1 for QDN: %s, role: %s, version: %d", qdn, role, version)
}
// attempt to insert. Due to race conditions with the check this could fail.
// That's OK, we're doing first write wins. The client will be messaged it
// needs to rebase.
_, err = vdb.Exec(insertStmt, qdn, role, version, data)
if err != nil {
return err
}
return nil
}
// Get a specific TUF record
func (vdb VersionDB) GetCurrent(qdn, tufRole string) (data []byte, err error) {
stmt := "SELECT `data` FROM `tuf_files` WHERE `qdn`=? AND `role`=? ORDER BY `version` DESC LIMIT 1;"
rows, err := vdb.Query(stmt, qdn, tufRole) // this should be a QueryRow()
if err != nil {
return nil, err
}
defer rows.Close()
// unique constraint on (qdn, role) will ensure only one row is returned (or none if no match is found)
if !rows.Next() {
return nil, nil
}
err = rows.Scan(&data)
if err != nil {
return nil, err
}
return data, nil
}

View File

@ -47,13 +47,14 @@ func (root *rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
ctx := context.WithValue(root.context, "repo", vars["imageName"])
ctx = context.WithValue(ctx, "trust", root.trust)
ctx = context.WithValue(ctx, "http.request", r)
access := buildAccessRecords(vars["imageName"], root.actions...)
var err error
if ctx, err = root.auth.Authorized(ctx, access...); err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
// access := buildAccessRecords(vars["imageName"], root.actions...)
// var err error
// if ctx, err = root.auth.Authorized(ctx, access...); err != nil {
// http.Error(w, err.Error(), http.StatusUnauthorized)
// return
// }
if err := root.handler(ctx, w, r); err != nil {
logrus.Error("[Vetinari] ", err.Error())
http.Error(w, err.Error(), err.HTTPStatus)