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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
_ "expvar"
|
_ "expvar"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -13,10 +14,12 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
_ "github.com/docker/distribution/registry/auth/token"
|
_ "github.com/docker/distribution/registry/auth/token"
|
||||||
"github.com/endophage/gotuf/signed"
|
"github.com/endophage/gotuf/signed"
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/docker/vetinari/config"
|
"github.com/docker/vetinari/config"
|
||||||
"github.com/docker/vetinari/server"
|
"github.com/docker/vetinari/server"
|
||||||
|
"github.com/docker/vetinari/server/version"
|
||||||
"github.com/docker/vetinari/signer"
|
"github.com/docker/vetinari/signer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -69,6 +72,12 @@ func main() {
|
||||||
trust = signed.NewEd25519()
|
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 {
|
for {
|
||||||
logrus.Info("[Vetinari] Starting Server")
|
logrus.Info("[Vetinari] Starting Server")
|
||||||
childCtx, cancel := context.WithCancel(ctx)
|
childCtx, cancel := context.WithCancel(ctx)
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,16 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/endophage/gotuf/data"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/docker/vetinari/errors"
|
"github.com/docker/vetinari/errors"
|
||||||
|
"github.com/docker/vetinari/server/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MainHandler is the default handler for the server
|
// 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>
|
// AddHandler accepts urls in the form /<imagename>/<tag>
|
||||||
func UpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) *errors.HTTPError {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHandler accepts urls in the form /<imagename>/<tuf file>.json
|
// GetHandler accepts urls in the form /<imagename>/<tuf file>.json
|
||||||
func GetHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) *errors.HTTPError {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,16 +97,17 @@ func run(ctx context.Context, addr, tlsCertFile, tlsKeyFile string, trust signed
|
||||||
}
|
}
|
||||||
tlsLsnr := tls.NewListener(lsnr, tlsConfig)
|
tlsLsnr := tls.NewListener(lsnr, tlsConfig)
|
||||||
|
|
||||||
ac, err := auth.GetAccessController("token", map[string]interface{}{})
|
var ac auth.AccessController = nil
|
||||||
if err != nil {
|
//ac, err := auth.GetAccessController("token", map[string]interface{}{})
|
||||||
return err
|
//if err != nil {
|
||||||
}
|
// return err
|
||||||
|
//}
|
||||||
hand := utils.RootHandlerFactory(ac, context.Background(), trust)
|
hand := utils.RootHandlerFactory(ac, context.Background(), trust)
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
// TODO (endophage): use correct regexes for image and tag names
|
// 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("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/{tufFile:(root.json|targets.json|timestamp.json|snapshot.json)}").Handler(hand(handlers.UpdateHandler, "push", "pull"))
|
r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.UpdateHandler, "push", "pull"))
|
||||||
|
|
||||||
svr := NewHTTPServer(
|
svr := NewHTTPServer(
|
||||||
http.Server{
|
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)
|
vars := mux.Vars(r)
|
||||||
ctx := context.WithValue(root.context, "repo", vars["imageName"])
|
ctx := context.WithValue(root.context, "repo", vars["imageName"])
|
||||||
ctx = context.WithValue(ctx, "trust", root.trust)
|
ctx = context.WithValue(ctx, "trust", root.trust)
|
||||||
|
ctx = context.WithValue(ctx, "http.request", r)
|
||||||
|
|
||||||
access := buildAccessRecords(vars["imageName"], root.actions...)
|
// access := buildAccessRecords(vars["imageName"], root.actions...)
|
||||||
var err error
|
// var err error
|
||||||
if ctx, err = root.auth.Authorized(ctx, access...); err != nil {
|
// if ctx, err = root.auth.Authorized(ctx, access...); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
// http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if err := root.handler(ctx, w, r); err != nil {
|
if err := root.handler(ctx, w, r); err != nil {
|
||||||
logrus.Error("[Vetinari] ", err.Error())
|
logrus.Error("[Vetinari] ", err.Error())
|
||||||
http.Error(w, err.Error(), err.HTTPStatus)
|
http.Error(w, err.Error(), err.HTTPStatus)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue