Merge pull request #428 from docker/merge-bootstrap-and-update

Combine bootstrapClient and tuf/client's Client.Update into a single Update function
This commit is contained in:
David Lawrence 2016-01-07 17:31:55 -08:00
commit 160980db89
2 changed files with 49 additions and 57 deletions

View File

@ -2,10 +2,10 @@ package client
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
@ -40,19 +40,12 @@ func init() {
)
}
// ErrRepoNotInitialized is returned when trying to can publish on an uninitialized
// ErrRepoNotInitialized is returned when trying to publish an uninitialized
// notary repository
type ErrRepoNotInitialized struct{}
// ErrRepoNotInitialized is returned when trying to can publish on an uninitialized
// notary repository
func (err *ErrRepoNotInitialized) Error() string {
return "Repository has not been initialized"
}
// ErrExpired is returned when the metadata for a role has expired
type ErrExpired struct {
signed.ErrExpired
func (err ErrRepoNotInitialized) Error() string {
return "repository has not been initialized"
}
// ErrInvalidRemoteRole is returned when the server is requested to manage
@ -66,14 +59,21 @@ func (e ErrInvalidRemoteRole) Error() string {
"notary does not support the server managing the %s key", e.Role)
}
// ErrRepositoryNotExist is returned when an action is taken on a remote
// repository that doesn't exist
type ErrRepositoryNotExist struct {
remote string
gun string
}
func (err ErrRepositoryNotExist) Error() string {
return fmt.Sprintf("%s does not have trust data for %s", err.remote, err.gun)
}
const (
tufDir = "tuf"
)
// ErrRepositoryNotExist gets returned when trying to make an action over a repository
/// that doesn't exist.
var ErrRepositoryNotExist = errors.New("repository does not exist")
// NotaryRepository stores all the information needed to operate on a notary
// repository.
type NotaryRepository struct {
@ -420,19 +420,11 @@ func (r *NotaryRepository) RemoveTarget(targetName string, roles ...string) erro
// subtree and also the "targets/x" subtree, as we will defer parsing it until
// we explicitly reach it in our iteration of the provided list of roles.
func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, error) {
c, err := r.bootstrapClient()
_, err := r.Update()
if err != nil {
return nil, err
}
err = c.Update()
if err != nil {
if err, ok := err.(signed.ErrExpired); ok {
return nil, ErrExpired{err}
}
return nil, err
}
if len(roles) == 0 {
roles = []string{data.CanonicalTargetsRole}
}
@ -488,19 +480,11 @@ func (r *NotaryRepository) listSubtree(targets map[string]*TargetWithRole, role
// will be returned
// See the IMPORTANT section on ListTargets above. Those roles also apply here.
func (r *NotaryRepository) GetTargetByName(name string, roles ...string) (*TargetWithRole, error) {
c, err := r.bootstrapClient()
c, err := r.Update()
if err != nil {
return nil, err
}
err = c.Update()
if err != nil {
if err, ok := err.(signed.ErrExpired); ok {
return nil, ErrExpired{err}
}
return nil, err
}
if len(roles) == 0 {
roles = append(roles, data.CanonicalTargetsRole)
}
@ -530,47 +514,33 @@ func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) {
// Conceptually it performs an operation similar to a `git rebase`
func (r *NotaryRepository) Publish() error {
var initialPublish bool
// attempt to initialize the repo from the remote store
c, err := r.bootstrapClient()
// update first before publishing
_, err := r.Update()
if err != nil {
if _, ok := err.(store.ErrMetaNotFound); ok {
// if the remote store return a 404 (translated into ErrMetaNotFound),
// there is no trust data for yet. Attempt to load it from disk.
// If the remote is not aware of the repo, then this is being published
// for the first time. Try to load from disk instead for publishing.
if _, ok := err.(ErrRepositoryNotExist); ok {
err := r.bootstrapRepo()
if err != nil {
// There are lots of reasons there might be an error, such as
// corrupt metadata. We need better errors from bootstrapRepo.
logrus.Debugf("Unable to load repository from local files: %s",
err.Error())
if _, ok := err.(store.ErrMetaNotFound); ok {
return &ErrRepoNotInitialized{}
return ErrRepoNotInitialized{}
}
return err
}
// We had local data but the server doesn't know about the repo yet,
// ensure we will push the initial root and targets file. Either or
// Ensure we will push the initial root and targets file. Either or
// both of the root and targets may not be marked as Dirty, since
// there may not be any changes that update them, so use a
// different boolean.
initialPublish = true
} else {
// The remote store returned an error other than 404. We're
// unable to determine if the repo has been initialized or not.
// We could not update, so we cannot publish.
logrus.Error("Could not publish Repository: ", err.Error())
return err
}
} else {
// If we were successfully able to bootstrap the client (which only pulls
// root.json), update it with the rest of the tuf metadata in
// preparation for applying the changelist.
err = c.Update()
if err != nil {
if err, ok := err.(signed.ErrExpired); ok {
return ErrExpired{err}
}
return err
}
}
cl, err := r.GetChangelist()
if err != nil {
return err
@ -745,6 +715,28 @@ func (r *NotaryRepository) saveMetadata(ignoreSnapshot bool) error {
return r.fileStore.SetMeta(data.CanonicalSnapshotRole, snapshotJSON)
}
// Update bootstraps a trust anchor (root.json) before updating all the
// metadata from the repo.
func (r *NotaryRepository) Update() (*tufclient.Client, error) {
c, err := r.bootstrapClient()
if err != nil {
if _, ok := err.(store.ErrMetaNotFound); ok {
host := r.baseURL
parsed, err := url.Parse(r.baseURL)
if err == nil {
host = parsed.Host // try to exclude the scheme and any paths
}
return nil, ErrRepositoryNotExist{remote: host, gun: r.gun}
}
return nil, err
}
err = c.Update()
if err != nil {
return nil, err
}
return c, nil
}
func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
var rootJSON []byte
remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)

View File

@ -1516,7 +1516,7 @@ func TestNotInitializedOnPublish(t *testing.T) {
err = repo.Publish()
require.Error(t, err)
require.IsType(t, &ErrRepoNotInitialized{}, err)
require.IsType(t, ErrRepoNotInitialized{}, err)
}
type cannotCreateKeys struct {