consistently use stdout and stderr (#502)

* consistently use stdout and stderr
Signed-off-by: Fabian Kammel <fk@edgeless.systems>
This commit is contained in:
Fabian Kammel 2022-11-10 10:27:24 +01:00 committed by GitHub
parent e011c7ef78
commit 81a5907f26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 61 additions and 59 deletions

View File

@ -29,11 +29,11 @@ type Upgrader struct {
measurementsUpdater measurementsUpdater
imageUpdater imageUpdater
writer io.Writer
outWriter io.Writer
}
// NewUpgrader returns a new Upgrader.
func NewUpgrader(writer io.Writer) (*Upgrader, error) {
func NewUpgrader(outWriter io.Writer) (*Upgrader, error) {
kubeConfig, err := clientcmd.BuildConfigFromFlags("", constants.AdminConfFilename)
if err != nil {
return nil, fmt.Errorf("building kubernetes config: %w", err)
@ -53,7 +53,7 @@ func NewUpgrader(writer io.Writer) (*Upgrader, error) {
return &Upgrader{
measurementsUpdater: &kubeMeasurementsUpdater{client: kubeClient},
imageUpdater: &kubeImageUpdater{client: unstructuredClient},
writer: writer,
outWriter: outWriter,
}, nil
}
@ -118,7 +118,7 @@ func (u *Upgrader) updateMeasurements(ctx context.Context, measurements map[uint
}
if !changed {
// measurements are the same, nothing to be done
fmt.Fprintln(u.writer, "Cluster is already using the chosen measurements, skipping measurements upgrade")
fmt.Fprintln(u.outWriter, "Cluster is already using the chosen measurements, skipping measurements upgrade")
return nil
}
}
@ -136,7 +136,7 @@ func (u *Upgrader) updateMeasurements(ctx context.Context, measurements map[uint
return fmt.Errorf("setting new measurements: %w", err)
}
fmt.Fprintln(u.writer, "Successfully updated the cluster's expected measurements")
fmt.Fprintln(u.outWriter, "Successfully updated the cluster's expected measurements")
return nil
}
@ -147,7 +147,7 @@ func (u *Upgrader) updateImage(ctx context.Context, image string) error {
}
if currentImageDefinition == image {
fmt.Fprintln(u.writer, "Cluster is already using the chosen image, skipping image upgrade")
fmt.Fprintln(u.outWriter, "Cluster is already using the chosen image, skipping image upgrade")
return nil
}
@ -156,7 +156,7 @@ func (u *Upgrader) updateImage(ctx context.Context, image string) error {
return fmt.Errorf("setting new image: %w", err)
}
fmt.Fprintln(u.writer, "Successfully updated the cluster's image, upgrades will be applied automatically")
fmt.Fprintln(u.outWriter, "Successfully updated the cluster's image, upgrades will be applied automatically")
return nil
}

View File

@ -76,7 +76,7 @@ func TestUpdateMeasurements(t *testing.T) {
upgrader := &Upgrader{
measurementsUpdater: tc.updater,
writer: &bytes.Buffer{},
outWriter: &bytes.Buffer{},
}
err := upgrader.updateMeasurements(context.Background(), tc.newMeasurements)
@ -203,7 +203,7 @@ func TestUpdateImage(t *testing.T) {
upgrader := &Upgrader{
imageUpdater: tc.updater,
writer: &bytes.Buffer{},
outWriter: &bytes.Buffer{},
}
err := upgrader.updateImage(context.Background(), tc.newImage)

View File

@ -63,7 +63,7 @@ func configFetchMeasurements(cmd *cobra.Command, verifier rekorVerifier, fileHan
}
if conf.IsDebugImage() {
cmd.Println("Configured image doesn't look like a released production image. Double check image before deploying to production.")
cmd.PrintErrln("Configured image doesn't look like a released production image. Double check image before deploying to production.")
}
if err := flags.updateURLs(conf); err != nil {
@ -79,8 +79,8 @@ func configFetchMeasurements(cmd *cobra.Command, verifier rekorVerifier, fileHan
}
if err := verifyWithRekor(cmd.Context(), verifier, hash); err != nil {
cmd.Printf("Ignoring Rekor related error: %v\n", err)
cmd.Println("Make sure the downloaded measurements are trustworthy!")
cmd.PrintErrf("Ignoring Rekor related error: %v\n", err)
cmd.PrintErrln("Make sure the downloaded measurements are trustworthy!")
}
conf.UpdateMeasurements(fetchedMeasurements)

View File

@ -7,7 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only
package cmd
import (
"fmt"
"strings"
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
@ -27,7 +26,7 @@ func newConfigInstanceTypesCmd() *cobra.Command {
}
func printSupportedInstanceTypes(cmd *cobra.Command, args []string) {
fmt.Printf(`AWS instance families:
cmd.Printf(`AWS instance families:
%v
Azure Confidential VM instance types:
%v

View File

@ -41,7 +41,7 @@ func NewCreateCmd() *cobra.Command {
func runCreate(cmd *cobra.Command, args []string) error {
fileHandler := file.NewHandler(afero.NewOsFs())
spinner := newSpinner(cmd.OutOrStdout())
spinner := newSpinner(cmd.ErrOrStderr())
defer spinner.Stop()
creator := cloudcmd.NewCreator(spinner)
@ -59,34 +59,34 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
return err
}
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath)
config, err := readConfig(cmd.ErrOrStderr(), fileHandler, flags.configPath)
if err != nil {
return fmt.Errorf("reading and validating config: %w", err)
}
var printedAWarning bool
if config.IsDebugImage() {
cmd.Println("Configured image doesn't look like a released production image. Double check image before deploying to production.")
cmd.PrintErrln("Configured image doesn't look like a released production image. Double check image before deploying to production.")
printedAWarning = true
}
if config.IsDebugCluster() {
cmd.Println("WARNING: Creating a debug cluster. This cluster is not secure and should only be used for debugging purposes.")
cmd.Println("DO NOT USE THIS CLUSTER IN PRODUCTION.")
cmd.PrintErrln("WARNING: Creating a debug cluster. This cluster is not secure and should only be used for debugging purposes.")
cmd.PrintErrln("DO NOT USE THIS CLUSTER IN PRODUCTION.")
printedAWarning = true
}
if config.IsAzureNonCVM() {
cmd.Println("Disabling Confidential VMs is insecure. Use only for evaluation purposes.")
cmd.PrintErrln("Disabling Confidential VMs is insecure. Use only for evaluation purposes.")
printedAWarning = true
if config.EnforcesIDKeyDigest() {
cmd.Println("Your config asks for enforcing the idkeydigest. This is only available on Confidential VMs. It will not be enforced.")
cmd.PrintErrln("Your config asks for enforcing the idkeydigest. This is only available on Confidential VMs. It will not be enforced.")
}
}
// Print an extra new line later to separate warnings from the prompt message of the create command
if printedAWarning {
cmd.Println("")
cmd.PrintErrln("")
}
provider := config.GetProvider()

View File

@ -79,7 +79,7 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
return err
}
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath)
config, err := readConfig(cmd.ErrOrStderr(), fileHandler, flags.configPath)
if err != nil {
return fmt.Errorf("reading and validating config: %w", err)
}
@ -94,13 +94,13 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
return fmt.Errorf("validating kubernetes version: %w", err)
}
if versions.IsPreviewK8sVersion(k8sVersion) {
cmd.Printf("Warning: Constellation with Kubernetes %v is still in preview. Use only for evaluation purposes.\n", k8sVersion)
cmd.PrintErrf("Warning: Constellation with Kubernetes %v is still in preview. Use only for evaluation purposes.\n", k8sVersion)
}
provider := config.GetProvider()
checker := license.NewChecker(quotaChecker, fileHandler)
if err := checker.CheckLicense(cmd.Context(), provider, config.Provider, cmd.Printf); err != nil {
cmd.Printf("License check failed: %v", err)
cmd.PrintErrf("License check failed: %v", err)
}
var sshUsers []*ssh.UserKey
@ -290,7 +290,7 @@ type masterSecret struct {
}
// readOrGenerateMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret.
func readOrGenerateMasterSecret(writer io.Writer, fileHandler file.Handler, filename string) (masterSecret, error) {
func readOrGenerateMasterSecret(outWriter io.Writer, fileHandler file.Handler, filename string) (masterSecret, error) {
if filename != "" {
var secret masterSecret
if err := fileHandler.ReadJSON(filename, &secret); err != nil {
@ -323,7 +323,7 @@ func readOrGenerateMasterSecret(writer io.Writer, fileHandler file.Handler, file
if err := fileHandler.WriteJSON(constants.MasterSecretFilename, secret, file.OptNone); err != nil {
return masterSecret{}, err
}
fmt.Fprintf(writer, "Your Constellation master secret was successfully written to ./%s\n", constants.MasterSecretFilename)
fmt.Fprintf(outWriter, "Your Constellation master secret was successfully written to ./%s\n", constants.MasterSecretFilename)
return secret, nil
}

View File

@ -50,7 +50,7 @@ func newMiniUpCmd() *cobra.Command {
}
func runUp(cmd *cobra.Command, args []string) error {
spinner := newSpinner(cmd.OutOrStdout())
spinner := newSpinner(cmd.ErrOrStderr())
defer spinner.Stop()
creator := cloudcmd.NewCreator(spinner)
@ -58,7 +58,7 @@ func runUp(cmd *cobra.Command, args []string) error {
}
func up(cmd *cobra.Command, creator cloudCreator, spinner spinnerInterf) error {
if err := checkSystemRequirements(cmd.OutOrStdout()); err != nil {
if err := checkSystemRequirements(cmd.ErrOrStderr()); err != nil {
return fmt.Errorf("system requirements not met: %w", err)
}
@ -163,7 +163,7 @@ func prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*config.Config
// check for existing config
if configPath != "" {
config, err := readConfig(cmd.OutOrStdout(), fileHandler, configPath)
config, err := readConfig(cmd.ErrOrStderr(), fileHandler, configPath)
if err != nil {
return nil, err
}
@ -178,7 +178,7 @@ func prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*config.Config
_, err = fileHandler.Stat(constants.ConfigFilename)
if err == nil {
// config already exists, prompt user to overwrite
cmd.Println("A config file already exists in the current workspace.")
cmd.PrintErrln("A config file already exists in the current workspace.")
ok, err := askToConfirm(cmd, "Do you want to overwrite it?")
if err != nil {
return nil, err
@ -194,7 +194,7 @@ func prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*config.Config
cmd.Printf("Using existing image at %s\n\n", imagePath)
} else if errors.Is(err, os.ErrNotExist) {
cmd.Printf("Downloading image to %s\n", imagePath)
if err := installImage(cmd.Context(), cmd.OutOrStdout(), versions.ConstellationQEMUImageURL, imagePath); err != nil {
if err := installImage(cmd.Context(), cmd.ErrOrStderr(), versions.ConstellationQEMUImageURL, imagePath); err != nil {
return nil, fmt.Errorf("downloading image to %s: %w", imagePath, err)
}
} else {
@ -226,10 +226,10 @@ func initializeMiniCluster(cmd *cobra.Command, fileHandler file.Handler, spinner
// clean up cluster resources if initialization fails
defer func() {
if retErr != nil {
cmd.Printf("An error occurred: %s\n", retErr)
cmd.Println("Attempting to roll back.")
cmd.PrintErrf("An error occurred: %s\n", retErr)
cmd.PrintErrln("Attempting to roll back.")
_ = runDown(cmd, []string{})
cmd.Printf("Rollback succeeded.\n\n")
cmd.PrintErrf("Rollback succeeded.\n\n")
}
}()
newDialer := func(validator *cloudcmd.Validator) *dialer.Dialer {
@ -247,7 +247,7 @@ func initializeMiniCluster(cmd *cobra.Command, fileHandler file.Handler, spinner
}
// installImage downloads the image from sourceURL to the destination.
func installImage(ctx context.Context, out io.Writer, sourceURL, destination string) error {
func installImage(ctx context.Context, errWriter io.Writer, sourceURL, destination string) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, sourceURL, nil)
if err != nil {
return fmt.Errorf("creating request: %w", err)
@ -270,7 +270,7 @@ func installImage(ctx context.Context, out io.Writer, sourceURL, destination str
bar := progressbar.NewOptions64(
resp.ContentLength,
progressbar.OptionSetWriter(out),
progressbar.OptionSetWriter(errWriter),
progressbar.OptionShowBytes(true),
progressbar.OptionSetPredictTime(true),
progressbar.OptionFullWidth(),
@ -282,7 +282,7 @@ func installImage(ctx context.Context, out io.Writer, sourceURL, destination str
BarEnd: "]",
}),
progressbar.OptionClearOnFinish(),
progressbar.OptionOnCompletion(func() { fmt.Fprintf(out, "Done.\n\n") }),
progressbar.OptionOnCompletion(func() { fmt.Fprintf(errWriter, "Done.\n\n") }),
)
defer bar.Close()

View File

@ -15,30 +15,30 @@ import (
"github.com/edgelesssys/constellation/v2/internal/file"
)
func readConfig(out io.Writer, fileHandler file.Handler, name string) (*config.Config, error) {
func readConfig(errWriter io.Writer, fileHandler file.Handler, name string) (*config.Config, error) {
cnf, err := config.FromFile(fileHandler, name)
if err != nil {
return nil, err
}
if err := validateConfig(out, cnf); err != nil {
if err := validateConfig(errWriter, cnf); err != nil {
return nil, err
}
return cnf, nil
}
func validateConfig(out io.Writer, cnf *config.Config) error {
func validateConfig(errWriter io.Writer, cnf *config.Config) error {
msgs, err := cnf.Validate()
if err != nil {
return fmt.Errorf("performing config validation: %w", err)
}
if len(msgs) > 0 {
fmt.Fprintln(out, "Invalid fields in config file:")
fmt.Fprintln(errWriter, "Invalid fields in config file:")
for _, m := range msgs {
fmt.Fprintln(out, "\t"+m)
fmt.Fprintln(errWriter, "\t"+m)
}
fmt.Fprintln(out, "Fix the invalid entries or generate a new configuration using `constellation config generate`")
fmt.Fprintln(errWriter, "Fix the invalid entries or generate a new configuration using `constellation config generate`")
return errors.New("invalid configuration")
}

View File

@ -66,7 +66,7 @@ func recover(
return err
}
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath)
config, err := readConfig(cmd.ErrOrStderr(), fileHandler, flags.configPath)
if err != nil {
return fmt.Errorf("reading and validating config: %w", err)
}

View File

@ -36,7 +36,7 @@ func NewTerminateCmd() *cobra.Command {
// runTerminate runs the terminate command.
func runTerminate(cmd *cobra.Command, args []string) error {
fileHandler := file.NewHandler(afero.NewOsFs())
spinner := newSpinner(cmd.OutOrStdout())
spinner := newSpinner(cmd.ErrOrStderr())
defer spinner.Stop()
terminator := cloudcmd.NewTerminator()

View File

@ -93,18 +93,18 @@ func upgradePlan(cmd *cobra.Command, planner upgradePlanner,
}
compatibleImages := getCompatibleImages(csp, version, images)
if len(compatibleImages) == 0 {
cmd.Println("No compatible images found to upgrade to.")
cmd.PrintErrln("No compatible images found to upgrade to.")
return nil
}
// get expected measurements for each image
if err := getCompatibleImageMeasurements(cmd.Context(), client, rekor, []byte(flags.cosignPubKey), compatibleImages); err != nil {
if err := getCompatibleImageMeasurements(cmd.Context(), cmd, client, rekor, []byte(flags.cosignPubKey), compatibleImages); err != nil {
return fmt.Errorf("fetching measurements for compatible images: %w", err)
}
// interactive mode
if flags.filePath == "" {
fmt.Fprintf(cmd.OutOrStdout(), "Current version: %s\n", version)
cmd.Printf("Current version: %s\n", version)
return upgradePlanInteractive(
&nopWriteCloser{cmd.OutOrStdout()},
io.NopCloser(cmd.InOrStdin()),
@ -179,7 +179,7 @@ func getCompatibleImages(csp cloudprovider.Provider, currentVersion string, imag
}
// getCompatibleImageMeasurements retrieves the expected measurements for each image.
func getCompatibleImageMeasurements(ctx context.Context, client *http.Client, rekor rekorVerifier, pubK []byte, images map[string]config.UpgradeConfig) error {
func getCompatibleImageMeasurements(ctx context.Context, cmd *cobra.Command, client *http.Client, rekor rekorVerifier, pubK []byte, images map[string]config.UpgradeConfig) error {
for idx, img := range images {
measurementsURL, err := url.Parse(constants.S3PublicBucket + strings.ToLower(img.Image) + "/measurements.yaml")
if err != nil {
@ -197,8 +197,8 @@ func getCompatibleImageMeasurements(ctx context.Context, client *http.Client, re
}
if err = verifyWithRekor(ctx, rekor, hash); err != nil {
fmt.Printf("Warning: Unable to verify '%s' in Rekor.\n", hash)
fmt.Printf("Make sure measurements are correct.\n")
cmd.PrintErrf("Warning: Unable to verify '%s' in Rekor.\n", hash)
cmd.PrintErrf("Make sure measurements are correct.\n")
}
images[idx] = img

View File

@ -21,6 +21,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/mod/semver"
@ -271,7 +272,7 @@ func TestGetCompatibleImageMeasurements(t *testing.T) {
pubK := []byte("-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUs5fDUIz9aiwrfr8BK4VjN7jE6sl\ngz7UuXsOin8+dB0SGrbNHy7TJToa2fAiIKPVLTOfvY75DqRAtffhO1fpBA==\n-----END PUBLIC KEY-----")
err := getCompatibleImageMeasurements(context.Background(), client, singleUUIDVerifier(), pubK, testImages)
err := getCompatibleImageMeasurements(context.Background(), &cobra.Command{}, client, singleUUIDVerifier(), pubK, testImages)
assert.NoError(err)
for _, image := range testImages {
@ -456,8 +457,10 @@ func TestUpgradePlan(t *testing.T) {
cmd := newUpgradePlanCmd()
cmd.SetContext(context.Background())
var out bytes.Buffer
cmd.SetOut(&out)
var outTarget bytes.Buffer
cmd.SetOut(&outTarget)
var errTarget bytes.Buffer
cmd.SetErr(&errTarget)
client := newTestClient(func(req *http.Request) *http.Response {
if req.URL.String() == imageReleaseURL {
@ -497,13 +500,13 @@ func TestUpgradePlan(t *testing.T) {
assert.NoError(err)
if !tc.wantUpgrade {
assert.Contains(out.String(), "No compatible images")
assert.Contains(errTarget.String(), "No compatible images")
return
}
var availableUpgrades map[string]config.UpgradeConfig
if tc.flags.filePath == "-" {
require.NoError(yaml.Unmarshal(out.Bytes(), &availableUpgrades))
require.NoError(yaml.Unmarshal(outTarget.Bytes(), &availableUpgrades))
} else {
require.NoError(fileHandler.ReadYAMLStrict(tc.flags.filePath, &availableUpgrades))
}

View File

@ -59,7 +59,7 @@ func verify(cmd *cobra.Command, fileHandler file.Handler, verifyClient verifyCli
return err
}
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath)
config, err := readConfig(cmd.ErrOrStderr(), fileHandler, flags.configPath)
if err != nil {
return fmt.Errorf("reading and validating config: %w", err)
}

View File

@ -28,7 +28,7 @@ func NewVersionCmd() *cobra.Command {
func runVersion(cmd *cobra.Command, args []string) {
buildInfo, ok := debug.ReadBuildInfo()
if !ok {
cmd.Printf("Unable to retrieve build info. Is buildvcs enabled?")
cmd.PrintErrf("Unable to retrieve build info. Is buildvcs enabled?")
return
}