mirror of https://github.com/docker/docs.git
runs with basic setup for demo
This commit is contained in:
parent
13c4d05b79
commit
801dd397ff
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue