fix: cli prompt termination exit code
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
This commit is contained in:
@ -3,7 +3,6 @@ package trust
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
@ -44,7 +43,11 @@ func revokeTrust(ctx context.Context, dockerCLI command.Cli, remote string, opti
|
||||
return fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
|
||||
}
|
||||
if imgRefAndAuth.Tag() == "" && !options.forceYes {
|
||||
deleteRemote := command.PromptForConfirmation(os.Stdin, dockerCLI.Out(), fmt.Sprintf("Please confirm you would like to delete all signature data for %s?", remote))
|
||||
deleteRemote, err := command.PromptForConfirmation(ctx, dockerCLI.In(), dockerCLI.Out(), fmt.Sprintf("Please confirm you would like to delete all signature data for %s?", remote))
|
||||
if err != nil {
|
||||
fmt.Fprintf(dockerCLI.Out(), "\nAborting action.\n")
|
||||
return errors.Wrap(err, "aborting action")
|
||||
}
|
||||
if !deleteRemote {
|
||||
fmt.Fprintf(dockerCLI.Out(), "\nAborting action.\n")
|
||||
return nil
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/cli/internal/test/notary"
|
||||
@ -12,6 +14,7 @@ import (
|
||||
"github.com/theupdateframework/notary/trustpinning"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/golden"
|
||||
)
|
||||
|
||||
func TestTrustRevokeCommandErrors(t *testing.T) {
|
||||
@ -148,3 +151,18 @@ func TestGetSignableRolesForTargetAndRemoveError(t *testing.T) {
|
||||
err = getSignableRolesForTargetAndRemove(target, notaryRepo)
|
||||
assert.Error(t, err, "client is offline")
|
||||
}
|
||||
|
||||
func TestRevokeTrustPromptTermination(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cmd := newRevokeCommand(cli)
|
||||
cmd.SetArgs([]string{"example/trust-demo"})
|
||||
test.TerminatePrompt(ctx, t, cmd, cli, func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
assert.ErrorIs(t, err, command.ErrPromptTerminated)
|
||||
})
|
||||
assert.Equal(t, cli.ErrBuffer().String(), "")
|
||||
golden.Assert(t, cli.OutBuffer().String(), "trust-revoke-prompt-termination.golden")
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ package trust
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
@ -76,6 +75,22 @@ func isLastSignerForReleases(roleWithSig data.Role, allRoles []client.RoleWithSi
|
||||
return counter < releasesRoleWithSigs.Threshold, nil
|
||||
}
|
||||
|
||||
func maybePromptForSignerRemoval(ctx context.Context, dockerCLI command.Cli, repoName, signerName string, isLastSigner, forceYes bool) (bool, error) {
|
||||
if isLastSigner && !forceYes {
|
||||
message := fmt.Sprintf("The signer \"%s\" signed the last released version of %s. "+
|
||||
"Removing this signer will make %s unpullable. "+
|
||||
"Are you sure you want to continue?",
|
||||
signerName, repoName, repoName,
|
||||
)
|
||||
removeSigner, err := command.PromptForConfirmation(ctx, dockerCLI.In(), dockerCLI.Out(), message)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return removeSigner, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// removeSingleSigner attempts to remove a single signer and returns whether signer removal happened.
|
||||
// The signer not being removed doesn't necessarily raise an error e.g. user choosing "No" when prompted for confirmation.
|
||||
func removeSingleSigner(ctx context.Context, dockerCLI command.Cli, repoName, signerName string, forceYes bool) (bool, error) {
|
||||
@ -110,28 +125,26 @@ func removeSingleSigner(ctx context.Context, dockerCLI command.Cli, repoName, si
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if ok, err := isLastSignerForReleases(role, allRoles); ok && !forceYes {
|
||||
removeSigner := command.PromptForConfirmation(os.Stdin, dockerCLI.Out(), fmt.Sprintf("The signer \"%s\" signed the last released version of %s. "+
|
||||
"Removing this signer will make %s unpullable. "+
|
||||
"Are you sure you want to continue?",
|
||||
signerName, repoName, repoName,
|
||||
))
|
||||
|
||||
if !removeSigner {
|
||||
fmt.Fprintf(dockerCLI.Out(), "\nAborting action.\n")
|
||||
return false, nil
|
||||
}
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err = notaryRepo.RemoveDelegationKeys(releasesRoleTUFName, role.KeyIDs); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err = notaryRepo.RemoveDelegationRole(signerDelegation); err != nil {
|
||||
isLastSigner, err := isLastSignerForReleases(role, allRoles)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err = notaryRepo.Publish(); err != nil {
|
||||
ok, err := maybePromptForSignerRemoval(ctx, dockerCLI, repoName, signerName, isLastSigner, forceYes)
|
||||
if err != nil || !ok {
|
||||
fmt.Fprintf(dockerCLI.Out(), "\nAborting action.\n")
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := notaryRepo.RemoveDelegationKeys(releasesRoleTUFName, role.KeyIDs); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := notaryRepo.RemoveDelegationRole(signerDelegation); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := notaryRepo.Publish(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
||||
@ -111,7 +111,8 @@ func TestIsLastSignerForReleases(t *testing.T) {
|
||||
releaserole.Name = releasesRoleTUFName
|
||||
releaserole.Threshold = 1
|
||||
allrole := []client.RoleWithSignatures{releaserole}
|
||||
lastsigner, _ := isLastSignerForReleases(role, allrole)
|
||||
lastsigner, err := isLastSignerForReleases(role, allrole)
|
||||
assert.Error(t, err, "all signed tags are currently revoked, use docker trust sign to fix")
|
||||
assert.Check(t, is.Equal(false, lastsigner))
|
||||
|
||||
role.KeyIDs = []string{"deadbeef"}
|
||||
@ -120,13 +121,15 @@ func TestIsLastSignerForReleases(t *testing.T) {
|
||||
releaserole.Signatures = []data.Signature{sig}
|
||||
releaserole.Threshold = 1
|
||||
allrole = []client.RoleWithSignatures{releaserole}
|
||||
lastsigner, _ = isLastSignerForReleases(role, allrole)
|
||||
lastsigner, err = isLastSignerForReleases(role, allrole)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(true, lastsigner))
|
||||
|
||||
sig.KeyID = "8badf00d"
|
||||
releaserole.Signatures = []data.Signature{sig}
|
||||
releaserole.Threshold = 1
|
||||
allrole = []client.RoleWithSignatures{releaserole}
|
||||
lastsigner, _ = isLastSignerForReleases(role, allrole)
|
||||
lastsigner, err = isLastSignerForReleases(role, allrole)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(false, lastsigner))
|
||||
}
|
||||
|
||||
2
cli/command/trust/testdata/trust-revoke-prompt-termination.golden
vendored
Normal file
2
cli/command/trust/testdata/trust-revoke-prompt-termination.golden
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
Please confirm you would like to delete all signature data for example/trust-demo? [y/N]
|
||||
Aborting action.
|
||||
Reference in New Issue
Block a user