diff --git a/api/client/trust.go b/api/client/trust.go index 83e052fbaf..ea910dda7d 100644 --- a/api/client/trust.go +++ b/api/client/trust.go @@ -375,34 +375,57 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, defer responseBody.Close() - targets := []target{} + // If it is a trusted push we would like to find the target entry which match the + // tag provided in the function and then do an AddTarget later. + target := &client.Target{} + // Count the times of calling for handleTarget, + // if it is called more that once, that should be considered an error in a trusted push. + cnt := 0 handleTarget := func(aux *json.RawMessage) { + cnt++ + if cnt > 1 { + // handleTarget should only be called one. This will be treated as an error. + return + } + var pushResult distribution.PushResult err := json.Unmarshal(*aux, &pushResult) if err == nil && pushResult.Tag != "" && pushResult.Digest.Validate() == nil { - targets = append(targets, target{ - reference: registry.ParseReference(pushResult.Tag), - digest: pushResult.Digest, - size: int64(pushResult.Size), - }) + h, err := hex.DecodeString(pushResult.Digest.Hex()) + if err != nil { + target = nil + return + } + target.Name = registry.ParseReference(pushResult.Tag).String() + target.Hashes = data.Hashes{string(pushResult.Digest.Algorithm()): h} + target.Length = int64(pushResult.Size) } } - err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, handleTarget) - if err != nil { + // We want trust signatures to always take an explicit tag, + // otherwise it will act as an untrusted push. + if tag == "" { + if err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, nil); err != nil { + return err + } + fmt.Fprintln(cli.out, "No tag specified, skipping trust metadata push") + return nil + } + + if err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, handleTarget); err != nil { return err } - if tag == "" { - fmt.Fprintf(cli.out, "No tag specified, skipping trust metadata push\n") - return nil + if cnt > 1 { + return fmt.Errorf("internal error: only one call to handleTarget expected") } - if len(targets) == 0 { - fmt.Fprintf(cli.out, "No targets found, skipping trust metadata push\n") + + if target == nil { + fmt.Fprintln(cli.out, "No targets found, please provide a specific tag in order to sign it") return nil } - fmt.Fprintf(cli.out, "Signing and pushing trust metadata\n") + fmt.Fprintln(cli.out, "Signing and pushing trust metadata") repo, err := cli.getNotaryRepository(repoInfo, authConfig, "push", "pull") if err != nil { @@ -410,21 +433,8 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, return err } - for _, target := range targets { - h, err := hex.DecodeString(target.digest.Hex()) - if err != nil { - return err - } - t := &client.Target{ - Name: target.reference.String(), - Hashes: data.Hashes{ - string(target.digest.Algorithm()): h, - }, - Length: int64(target.size), - } - if err := repo.AddTarget(t, releasesRole); err != nil { - return err - } + if err := repo.AddTarget(target, releasesRole); err != nil { + return err } err = repo.Publish()