AB#2033 User-friendly wrap and reword errors

fix: readOrGenerated function signature
This commit is contained in:
Christoph Meyer 2022-06-09 14:10:42 +00:00 committed by cm
parent 9441e46e4b
commit 1e11188dac
10 changed files with 61 additions and 56 deletions

View file

@ -1,6 +1,8 @@
package cmd package cmd
import ( import (
"fmt"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/config" "github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
@ -50,7 +52,7 @@ func configGenerate(cmd *cobra.Command, fileHandler file.Handler, provider cloud
if flags.file == "-" { if flags.file == "-" {
content, err := encoder.NewEncoder(conf).Encode() content, err := encoder.NewEncoder(conf).Encode()
if err != nil { if err != nil {
return err return fmt.Errorf("encoding config content: %w", err)
} }
_, err = cmd.OutOrStdout().Write(content) _, err = cmd.OutOrStdout().Write(content)
return err return err
@ -62,7 +64,7 @@ func configGenerate(cmd *cobra.Command, fileHandler file.Handler, provider cloud
func parseGenerateFlags(cmd *cobra.Command) (generateFlags, error) { func parseGenerateFlags(cmd *cobra.Command) (generateFlags, error) {
file, err := cmd.Flags().GetString("file") file, err := cmd.Flags().GetString("file")
if err != nil { if err != nil {
return generateFlags{}, err return generateFlags{}, fmt.Errorf("parsing config generate flags: %w", err)
} }
return generateFlags{ return generateFlags{
file: file, file: file,

View file

@ -70,7 +70,7 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider) config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil { if err != nil {
return err return fmt.Errorf("reading and validating config: %w", err)
} }
if !flags.yes { if !flags.yes {
@ -105,7 +105,7 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (createFlags, error) { func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (createFlags, error) {
controllerCount, err := cmd.Flags().GetInt("control-plane-nodes") controllerCount, err := cmd.Flags().GetInt("control-plane-nodes")
if err != nil { if err != nil {
return createFlags{}, err return createFlags{}, fmt.Errorf("parsing number of control-plane nodes: %w", err)
} }
if controllerCount < constants.MinControllerCount { if controllerCount < constants.MinControllerCount {
return createFlags{}, fmt.Errorf("number of control-plane nodes must be at least %d", constants.MinControllerCount) return createFlags{}, fmt.Errorf("number of control-plane nodes must be at least %d", constants.MinControllerCount)
@ -113,7 +113,7 @@ func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (crea
workerCount, err := cmd.Flags().GetInt("worker-nodes") workerCount, err := cmd.Flags().GetInt("worker-nodes")
if err != nil { if err != nil {
return createFlags{}, err return createFlags{}, fmt.Errorf("parsing number of worker nodes: %w", err)
} }
if workerCount < constants.MinWorkerCount { if workerCount < constants.MinWorkerCount {
return createFlags{}, fmt.Errorf("number of worker nodes must be at least %d", constants.MinWorkerCount) return createFlags{}, fmt.Errorf("number of worker nodes must be at least %d", constants.MinWorkerCount)
@ -121,7 +121,7 @@ func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (crea
insType, err := cmd.Flags().GetString("instance-type") insType, err := cmd.Flags().GetString("instance-type")
if err != nil { if err != nil {
return createFlags{}, err return createFlags{}, fmt.Errorf("parsing instance type argument: %w", err)
} }
if insType == "" { if insType == "" {
insType = defaultInstanceType(provider) insType = defaultInstanceType(provider)
@ -132,7 +132,7 @@ func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (crea
name, err := cmd.Flags().GetString("name") name, err := cmd.Flags().GetString("name")
if err != nil { if err != nil {
return createFlags{}, err return createFlags{}, fmt.Errorf("parsing name argument: %w", err)
} }
if len(name) > constants.ConstellationNameLength { if len(name) > constants.ConstellationNameLength {
return createFlags{}, fmt.Errorf( return createFlags{}, fmt.Errorf(
@ -143,12 +143,12 @@ func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (crea
yes, err := cmd.Flags().GetBool("yes") yes, err := cmd.Flags().GetBool("yes")
if err != nil { if err != nil {
return createFlags{}, err return createFlags{}, fmt.Errorf("%w; Set '-yes' without a value to automatically confirm", err)
} }
configPath, err := cmd.Flags().GetString("config") configPath, err := cmd.Flags().GetString("config")
if err != nil { if err != nil {
return createFlags{}, err return createFlags{}, fmt.Errorf("parsing config path argument: %w", err)
} }
return createFlags{ return createFlags{
@ -192,7 +192,7 @@ func checkDirClean(fileHandler file.Handler) error {
return fmt.Errorf("file '%s' already exists in working directory, run 'constellation terminate' before creating a new one", constants.AdminConfFilename) return fmt.Errorf("file '%s' already exists in working directory, run 'constellation terminate' before creating a new one", constants.AdminConfFilename)
} }
if _, err := fileHandler.Stat(constants.MasterSecretFilename); !errors.Is(err, fs.ErrNotExist) { if _, err := fileHandler.Stat(constants.MasterSecretFilename); !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("file '%s' already exists in working directory, clean it up first", constants.MasterSecretFilename) return fmt.Errorf("file '%s' already exists in working directory. Constellation won't overwrite previous master secrets. Move it somewhere or delete it before creating a new cluster", constants.MasterSecretFilename)
} }
return nil return nil

View file

@ -80,16 +80,16 @@ func initialize(ctx context.Context, cmd *cobra.Command, protCl protoClient, ser
var stat state.ConstellationState var stat state.ConstellationState
err = fileHandler.ReadJSON(constants.StateFilename, &stat) err = fileHandler.ReadJSON(constants.StateFilename, &stat)
if errors.Is(err, fs.ErrNotExist) { if errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("nothing to initialize: %w", err) return fmt.Errorf("missing Constellation state file: %w. Please do 'constellation create ...' before 'constellation init'", err)
} else if err != nil { } else if err != nil {
return err return fmt.Errorf("loading Constellation state file: %w", err)
} }
provider := cloudprovider.FromString(stat.CloudProvider) provider := cloudprovider.FromString(stat.CloudProvider)
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider) config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil { if err != nil {
return err return fmt.Errorf("reading and validating config: %w", err)
} }
var sshUsers []*ssh.UserKey var sshUsers []*ssh.UserKey
@ -166,7 +166,7 @@ func initialize(ctx context.Context, cmd *cobra.Command, protCl protoClient, ser
if flags.autoconfigureWG { if flags.autoconfigureWG {
if err := vpnHandler.Apply(vpnConfig); err != nil { if err := vpnHandler.Apply(vpnConfig); err != nil {
return err return fmt.Errorf("configuring WireGuard: %w", err)
} }
} }
@ -247,7 +247,7 @@ type activationResult struct {
func writeWGQuickFile(fileHandler file.Handler, vpnHandler vpnHandler, vpnConfig *wgquick.Config) error { func writeWGQuickFile(fileHandler file.Handler, vpnHandler vpnHandler, vpnConfig *wgquick.Config) error {
data, err := vpnHandler.Marshal(vpnConfig) data, err := vpnHandler.Marshal(vpnConfig)
if err != nil { if err != nil {
return err return fmt.Errorf("marshalling VPN config: %w", err)
} }
return fileHandler.Write(constants.WGQuickConfigFilename, data, file.OptNone) return fileHandler.Write(constants.WGQuickConfigFilename, data, file.OptNone)
} }
@ -285,7 +285,7 @@ func writeRow(wr io.Writer, col1 string, col2 string) {
func evalFlagArgs(cmd *cobra.Command, fileHandler file.Handler) (initFlags, error) { func evalFlagArgs(cmd *cobra.Command, fileHandler file.Handler) (initFlags, error) {
userPrivKeyPath, err := cmd.Flags().GetString("privatekey") userPrivKeyPath, err := cmd.Flags().GetString("privatekey")
if err != nil { if err != nil {
return initFlags{}, err return initFlags{}, fmt.Errorf("parsing privatekey path argument: %w", err)
} }
userPrivKey, userPubKey, err := readOrGenerateVPNKey(fileHandler, userPrivKeyPath) userPrivKey, userPubKey, err := readOrGenerateVPNKey(fileHandler, userPrivKeyPath)
if err != nil { if err != nil {
@ -293,23 +293,23 @@ func evalFlagArgs(cmd *cobra.Command, fileHandler file.Handler) (initFlags, erro
} }
autoconfigureWG, err := cmd.Flags().GetBool("wg-autoconfig") autoconfigureWG, err := cmd.Flags().GetBool("wg-autoconfig")
if err != nil { if err != nil {
return initFlags{}, err return initFlags{}, fmt.Errorf("parsing wg-autoconfig argument: %w", err)
} }
masterSecretPath, err := cmd.Flags().GetString("master-secret") masterSecretPath, err := cmd.Flags().GetString("master-secret")
if err != nil { if err != nil {
return initFlags{}, err return initFlags{}, fmt.Errorf("parsing master-secret path argument: %w", err)
} }
masterSecret, err := readOrGeneratedMasterSecret(cmd.OutOrStdout(), fileHandler, masterSecretPath) masterSecret, err := readOrGenerateMasterSecret(cmd.OutOrStdout(), fileHandler, masterSecretPath)
if err != nil { if err != nil {
return initFlags{}, err return initFlags{}, fmt.Errorf("parsing or generating master mastersecret from file %s: %w", masterSecretPath, err)
} }
autoscale, err := cmd.Flags().GetBool("autoscale") autoscale, err := cmd.Flags().GetBool("autoscale")
if err != nil { if err != nil {
return initFlags{}, err return initFlags{}, fmt.Errorf("parsing autoscale argument: %w", err)
} }
configPath, err := cmd.Flags().GetString("config") configPath, err := cmd.Flags().GetString("config")
if err != nil { if err != nil {
return initFlags{}, err return initFlags{}, fmt.Errorf("parsing config path argument: %w", err)
} }
return initFlags{ return initFlags{
@ -337,17 +337,17 @@ func readOrGenerateVPNKey(fileHandler file.Handler, privKeyPath string) (privKey
if privKeyPath == "" { if privKeyPath == "" {
privKeyParsed, err = wgtypes.GeneratePrivateKey() privKeyParsed, err = wgtypes.GeneratePrivateKey()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, fmt.Errorf("generating WireGuard private key: %w", err)
} }
privKey = []byte(privKeyParsed.String()) privKey = []byte(privKeyParsed.String())
} else { } else {
privKey, err = fileHandler.Read(privKeyPath) privKey, err = fileHandler.Read(privKeyPath)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, fmt.Errorf("reading the VPN private key: %w", err)
} }
privKeyParsed, err = wgtypes.ParseKey(string(privKey)) privKeyParsed, err = wgtypes.ParseKey(string(privKey))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, fmt.Errorf("parsing the WireGuard private key: %w", err)
} }
} }
@ -367,8 +367,8 @@ func ipsToEndpoints(ips []string, port string) []string {
return endpoints return endpoints
} }
// readOrGeneratedMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret. // readOrGenerateMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret.
func readOrGeneratedMasterSecret(w io.Writer, fileHandler file.Handler, filename string) ([]byte, error) { func readOrGenerateMasterSecret(writer io.Writer, fileHandler file.Handler, filename string) ([]byte, error) {
if filename != "" { if filename != "" {
// Try to read the base64 secret from file // Try to read the base64 secret from file
encodedSecret, err := fileHandler.Read(filename) encodedSecret, err := fileHandler.Read(filename)
@ -393,7 +393,7 @@ func readOrGeneratedMasterSecret(w io.Writer, fileHandler file.Handler, filename
if err := fileHandler.Write(constants.MasterSecretFilename, []byte(base64.StdEncoding.EncodeToString(masterSecret)), file.OptNone); err != nil { if err := fileHandler.Write(constants.MasterSecretFilename, []byte(base64.StdEncoding.EncodeToString(masterSecret)), file.OptNone); err != nil {
return nil, err return nil, err
} }
fmt.Fprintf(w, "Your Constellation master secret was successfully written to ./%s\n", constants.MasterSecretFilename) fmt.Fprintf(writer, "Your Constellation master secret was successfully written to ./%s\n", constants.MasterSecretFilename)
return masterSecret, nil return masterSecret, nil
} }

View file

@ -414,7 +414,7 @@ func TestReadOrGenerateVPNKey(t *testing.T) {
assert.NotEmpty(pubK) assert.NotEmpty(pubK)
} }
func TestReadOrGeneratedMasterSecret(t *testing.T) { func TestReadOrGenerateMasterSecret(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
filename string filename string
filecontent string filecontent string
@ -484,7 +484,7 @@ func TestReadOrGeneratedMasterSecret(t *testing.T) {
} }
var out bytes.Buffer var out bytes.Buffer
secret, err := readOrGeneratedMasterSecret(&out, fileHandler, tc.filename) secret, err := readOrGenerateMasterSecret(&out, fileHandler, tc.filename)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)

View file

@ -24,7 +24,7 @@ func readConfig(out io.Writer, fileHandler file.Handler, name string, provider c
func validateConfig(out io.Writer, cnf *config.Config, provider cloudprovider.Provider) error { func validateConfig(out io.Writer, cnf *config.Config, provider cloudprovider.Provider) error {
msgs, err := cnf.Validate() msgs, err := cnf.Validate()
if err != nil { if err != nil {
return err return fmt.Errorf("performing config validation: %w", err)
} }
if len(msgs) > 0 { if len(msgs) > 0 {
@ -32,7 +32,7 @@ func validateConfig(out io.Writer, cnf *config.Config, provider cloudprovider.Pr
for _, m := range msgs { for _, m := range msgs {
fmt.Fprintln(out, "\t"+m) fmt.Fprintln(out, "\t"+m)
} }
return errors.New("invalid configuration") return errors.New("invalid configuration. Fix the invalid entries or generate a new configuration using `constellation config generate`")
} }
if provider != cloudprovider.Unknown && !cnf.HasProvider(provider) { if provider != cloudprovider.Unknown && !cnf.HasProvider(provider) {

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt"
"regexp" "regexp"
"strings" "strings"
@ -60,7 +61,7 @@ func recover(ctx context.Context, cmd *cobra.Command, fileHandler file.Handler,
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider) config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil { if err != nil {
return err return fmt.Errorf("reading and validating config: %w", err)
} }
validators, err := cloudcmd.NewValidators(provider, config) validators, err := cloudcmd.NewValidators(provider, config)
@ -89,16 +90,16 @@ func recover(ctx context.Context, cmd *cobra.Command, fileHandler file.Handler,
func parseRecoverFlags(cmd *cobra.Command, fileHandler file.Handler) (recoverFlags, error) { func parseRecoverFlags(cmd *cobra.Command, fileHandler file.Handler) (recoverFlags, error) {
endpoint, err := cmd.Flags().GetString("endpoint") endpoint, err := cmd.Flags().GetString("endpoint")
if err != nil { if err != nil {
return recoverFlags{}, err return recoverFlags{}, fmt.Errorf("parsing endpoint argument: %w", err)
} }
endpoint, err = validateEndpoint(endpoint, constants.CoordinatorPort) endpoint, err = validateEndpoint(endpoint, constants.CoordinatorPort)
if err != nil { if err != nil {
return recoverFlags{}, err return recoverFlags{}, fmt.Errorf("validating endpoint argument: %w", err)
} }
diskUUID, err := cmd.Flags().GetString("disk-uuid") diskUUID, err := cmd.Flags().GetString("disk-uuid")
if err != nil { if err != nil {
return recoverFlags{}, err return recoverFlags{}, fmt.Errorf("parsing disk-uuid argument: %w", err)
} }
if match := diskUUIDRegexp.MatchString(diskUUID); !match { if match := diskUUIDRegexp.MatchString(diskUUID); !match {
return recoverFlags{}, errors.New("flag '--disk-uuid' isn't a valid LUKS UUID") return recoverFlags{}, errors.New("flag '--disk-uuid' isn't a valid LUKS UUID")
@ -107,16 +108,16 @@ func parseRecoverFlags(cmd *cobra.Command, fileHandler file.Handler) (recoverFla
masterSecretPath, err := cmd.Flags().GetString("master-secret") masterSecretPath, err := cmd.Flags().GetString("master-secret")
if err != nil { if err != nil {
return recoverFlags{}, err return recoverFlags{}, fmt.Errorf("parsing master-secret path argument: %w", err)
} }
masterSecret, err := readMasterSecret(fileHandler, masterSecretPath) masterSecret, err := readMasterSecret(fileHandler, masterSecretPath)
if err != nil { if err != nil {
return recoverFlags{}, err return recoverFlags{}, fmt.Errorf("reading the master secret from file %s: %w", masterSecretPath, err)
} }
configPath, err := cmd.Flags().GetString("config") configPath, err := cmd.Flags().GetString("config")
if err != nil { if err != nil {
return recoverFlags{}, err return recoverFlags{}, fmt.Errorf("parsing config path argument: %w", err)
} }
return recoverFlags{ return recoverFlags{

View file

@ -38,28 +38,28 @@ func runTerminate(cmd *cobra.Command, args []string) error {
func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.Handler) error { func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.Handler) error {
var stat state.ConstellationState var stat state.ConstellationState
if err := fileHandler.ReadJSON(constants.StateFilename, &stat); err != nil { if err := fileHandler.ReadJSON(constants.StateFilename, &stat); err != nil {
return err return fmt.Errorf("reading Constellation state: %w", err)
} }
cmd.Println("Terminating ...") cmd.Println("Terminating ...")
if err := terminator.Terminate(cmd.Context(), stat); err != nil { if err := terminator.Terminate(cmd.Context(), stat); err != nil {
return err return fmt.Errorf("terminating Constellation cluster: %w", err)
} }
cmd.Println("Your Constellation cluster was terminated successfully.") cmd.Println("Your Constellation cluster was terminated successfully.")
var retErr error var retErr error
if err := fileHandler.Remove(constants.StateFilename); err != nil { if err := fileHandler.Remove(constants.StateFilename); err != nil {
retErr = multierr.Append(err, fmt.Errorf("failed to remove file '%s', please remove manually", constants.StateFilename)) retErr = multierr.Append(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", constants.StateFilename))
} }
if err := fileHandler.Remove(constants.AdminConfFilename); err != nil && !errors.Is(err, fs.ErrNotExist) { if err := fileHandler.Remove(constants.AdminConfFilename); err != nil && !errors.Is(err, fs.ErrNotExist) {
retErr = multierr.Append(err, fmt.Errorf("failed to remove file '%s', please remove manually", constants.AdminConfFilename)) retErr = multierr.Append(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", constants.AdminConfFilename))
} }
if err := fileHandler.Remove(constants.WGQuickConfigFilename); err != nil && !errors.Is(err, fs.ErrNotExist) { if err := fileHandler.Remove(constants.WGQuickConfigFilename); err != nil && !errors.Is(err, fs.ErrNotExist) {
retErr = multierr.Append(err, fmt.Errorf("failed to remove file '%s', please remove manually", constants.WGQuickConfigFilename)) retErr = multierr.Append(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", constants.WGQuickConfigFilename))
} }
return retErr return retErr

View file

@ -51,7 +51,7 @@ func verify(ctx context.Context, cmd *cobra.Command, provider cloudprovider.Prov
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider) config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil { if err != nil {
return err return fmt.Errorf("reading and validating config: %w", err)
} }
validators, err := cloudcmd.NewValidators(provider, config) validators, err := cloudcmd.NewValidators(provider, config)
@ -83,28 +83,28 @@ func verify(ctx context.Context, cmd *cobra.Command, provider cloudprovider.Prov
func parseVerifyFlags(cmd *cobra.Command) (verifyFlags, error) { func parseVerifyFlags(cmd *cobra.Command) (verifyFlags, error) {
ownerID, err := cmd.Flags().GetString("owner-id") ownerID, err := cmd.Flags().GetString("owner-id")
if err != nil { if err != nil {
return verifyFlags{}, err return verifyFlags{}, fmt.Errorf("parsing owner-id argument: %w", err)
} }
clusterID, err := cmd.Flags().GetString("unique-id") clusterID, err := cmd.Flags().GetString("unique-id")
if err != nil { if err != nil {
return verifyFlags{}, err return verifyFlags{}, fmt.Errorf("parsing unique-id argument: %w", err)
} }
if ownerID == "" && clusterID == "" { if ownerID == "" && clusterID == "" {
return verifyFlags{}, errors.New("neither owner ID nor unique ID provided to verify the cluster") return verifyFlags{}, errors.New("neither owner-id nor unique-id provided to verify the cluster")
} }
endpoint, err := cmd.Flags().GetString("node-endpoint") endpoint, err := cmd.Flags().GetString("node-endpoint")
if err != nil { if err != nil {
return verifyFlags{}, err return verifyFlags{}, fmt.Errorf("parsing node-endpoint argument: %w", err)
} }
endpoint, err = validateEndpoint(endpoint, constants.CoordinatorPort) endpoint, err = validateEndpoint(endpoint, constants.CoordinatorPort)
if err != nil { if err != nil {
return verifyFlags{}, err return verifyFlags{}, fmt.Errorf("validating endpoint argument: %w", err)
} }
configPath, err := cmd.Flags().GetString("config") configPath, err := cmd.Flags().GetString("config")
if err != nil { if err != nil {
return verifyFlags{}, err return verifyFlags{}, fmt.Errorf("parsing config path argument: %w", err)
} }
return verifyFlags{ return verifyFlags{

View file

@ -126,7 +126,7 @@ func (m *Metadata) GetInstance(ctx context.Context, providerID string) (cloudtyp
if scaleSetErr == nil { if scaleSetErr == nil {
return instance, nil return instance, nil
} }
return cloudtypes.Instance{}, fmt.Errorf("retrieving instance given providerID %v as either single vm or scale set vm: %v; %v", providerID, singleErr, scaleSetErr) return cloudtypes.Instance{}, fmt.Errorf("retrieving instance given providerID %v as either single VM or scale set VM: %v; %v", providerID, singleErr, scaleSetErr)
} }
// SignalRole signals the constellation role via cloud provider metadata. // SignalRole signals the constellation role via cloud provider metadata.

View file

@ -90,16 +90,18 @@ func (i *osInstaller) extractArchive(archivePath, prefix string, perm fs.FileMod
if len(header.Name) == 0 { if len(header.Name) == 0 {
return errors.New("cannot create dir for empty path") return errors.New("cannot create dir for empty path")
} }
if err := i.fs.Mkdir(path.Join(prefix, header.Name), fs.FileMode(header.Mode)&perm); err != nil && !errors.Is(err, os.ErrExist) { prefixedPath := path.Join(prefix, header.Name)
return fmt.Errorf("creating folder %s: %w", path.Join(prefix, header.Name), err) if err := i.fs.Mkdir(prefixedPath, fs.FileMode(header.Mode)&perm); err != nil && !errors.Is(err, os.ErrExist) {
return fmt.Errorf("creating folder %q: %w", prefixedPath, err)
} }
case tar.TypeReg: case tar.TypeReg:
if len(header.Name) == 0 { if len(header.Name) == 0 {
return errors.New("cannot create file for empty path") return errors.New("cannot create file for empty path")
} }
out, err := i.fs.OpenFile(path.Join(prefix, header.Name), os.O_WRONLY|os.O_CREATE, fs.FileMode(header.Mode)) prefixedPath := path.Join(prefix, header.Name)
out, err := i.fs.OpenFile(prefixedPath, os.O_WRONLY|os.O_CREATE, fs.FileMode(header.Mode))
if err != nil { if err != nil {
return fmt.Errorf("creating file %s for writing: %w", path.Join(prefix, header.Name), err) return fmt.Errorf("creating file %q for writing: %w", prefixedPath, err)
} }
defer out.Close() defer out.Close()
if _, err := io.Copy(out, tarReader); err != nil { if _, err := io.Copy(out, tarReader); err != nil {