diff --git a/client/client.go b/client/client.go index 7a645e5024..07edc6e972 100644 --- a/client/client.go +++ b/client/client.go @@ -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) diff --git a/client/client_test.go b/client/client_test.go index d5f54b76a7..0cdb441412 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -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 {