Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cmd/influxd): add --overwrite-existing-v2 flag to influxd upgrade #20473

Merged
merged 6 commits into from
Jan 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Replacement `tsi1` indexes will be automatically generated on startup for shards

### Features

1. [19811](https://github.com/influxdata/influxdb/pull/19811): Add Geo graph type to be able to store in Dashboard cells
1. [19811](https://github.com/influxdata/influxdb/pull/19811): Add Geo graph type to be able to store in Dashboard cells.
1. [20473](https://github.com/influxdata/influxdb/pull/20473): Add `--overwrite-existing-v2` flag to `influxd upgrade` to overwrite existing files at output paths (instead of aborting).

### Bug Fixes

Expand Down
119 changes: 87 additions & 32 deletions cmd/influxd/upgrade/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,16 @@ type optionsV2 struct {
enginePath string
cqPath string
configPath string
userName string
password string
orgName string
bucket string
orgID influxdb.ID
userID influxdb.ID
token string
retention string
rmConflicts bool

userName string
password string
orgName string
bucket string
orgID influxdb.ID
userID influxdb.ID
token string
retention string
}

type options struct {
Expand Down Expand Up @@ -264,6 +266,12 @@ func NewCommand(v *viper.Viper) *cobra.Command {
Desc: "skip the confirmation prompt",
Short: 'f',
},
{
DestP: &options.target.rmConflicts,
Flag: "overwrite-existing-v2",
Default: false,
Desc: "if files are present at an output path, overwrite them instead of aborting the upgrade process",
},
}

cli.BindOptions(v, cmd, opts)
Expand Down Expand Up @@ -373,8 +381,14 @@ func runUpgradeE(cmd *cobra.Command, options *options, verbose bool) error {
options.source.dbURL = (&configV1{}).dbURL()
}

err = validatePaths(&options.source, &options.target)
if err != nil {
if err := options.source.validatePaths(); err != nil {
return err
}
checkV2paths := options.target.validatePaths
if options.target.rmConflicts {
checkV2paths = options.target.clearPaths
}
if err := checkV2paths(); err != nil {
return err
}

Expand Down Expand Up @@ -478,54 +492,95 @@ func runUpgradeE(cmd *cobra.Command, options *options, verbose bool) error {
return nil
}

// validatePaths ensures that all filesystem paths provided as input
// are usable by the upgrade command
func validatePaths(sourceOpts *optionsV1, targetOpts *optionsV2) error {
if sourceOpts.dbDir != "" {
fi, err := os.Stat(sourceOpts.dbDir)
// validatePaths ensures that all paths pointing to V1 inputs are usable by the upgrade command.
func (o *optionsV1) validatePaths() error {
if o.dbDir != "" {
fi, err := os.Stat(o.dbDir)
if err != nil {
return fmt.Errorf("1.x DB dir '%s' does not exist", sourceOpts.dbDir)
return fmt.Errorf("1.x DB dir '%s' does not exist", o.dbDir)
}
if !fi.IsDir() {
return fmt.Errorf("1.x DB dir '%s' is not a directory", sourceOpts.dbDir)
return fmt.Errorf("1.x DB dir '%s' is not a directory", o.dbDir)
}
}

metaDb := filepath.Join(sourceOpts.metaDir, "meta.db")
metaDb := filepath.Join(o.metaDir, "meta.db")
_, err := os.Stat(metaDb)
if err != nil {
return fmt.Errorf("1.x meta.db '%s' does not exist", metaDb)
}

if targetOpts.configPath != "" {
if _, err := os.Stat(targetOpts.configPath); err == nil {
return fmt.Errorf("file present at target path for upgraded 2.x config file '%s'", targetOpts.configPath)
return nil
}

// validatePaths ensures that none of the paths pointing to V2 outputs refer to existing files.
func (o *optionsV2) validatePaths() error {
if o.configPath != "" {
if _, err := os.Stat(o.configPath); err == nil {
return fmt.Errorf("file present at target path for upgraded 2.x config file %q", o.configPath)
} else if !os.IsNotExist(err) {
return fmt.Errorf("error checking for existing file at %q: %w", o.configPath, err)
}
}

if _, err = os.Stat(targetOpts.boltPath); err == nil {
return fmt.Errorf("file present at target path for upgraded 2.x bolt DB: '%s'", targetOpts.boltPath)
if _, err := os.Stat(o.boltPath); err == nil {
return fmt.Errorf("file present at target path for upgraded 2.x bolt DB: %q", o.boltPath)
} else if !os.IsNotExist(err) {
return fmt.Errorf("error checking for existing file at %q: %w", o.boltPath, err)
}

if fi, err := os.Stat(targetOpts.enginePath); err == nil {
if fi, err := os.Stat(o.enginePath); err == nil {
danxmoran marked this conversation as resolved.
Show resolved Hide resolved
if !fi.IsDir() {
return fmt.Errorf("upgraded 2.x engine path '%s' is not a directory", targetOpts.enginePath)
return fmt.Errorf("upgraded 2.x engine path %q is not a directory", o.enginePath)
}
entries, err := ioutil.ReadDir(targetOpts.enginePath)
entries, err := ioutil.ReadDir(o.enginePath)
if err != nil {
return err
return fmt.Errorf("error checking contents of existing engine directory %q: %w", o.enginePath, err)
}
if len(entries) > 0 {
return fmt.Errorf("upgraded 2.x engine directory '%s' must be empty", targetOpts.enginePath)
return fmt.Errorf("upgraded 2.x engine directory %q must be empty", o.enginePath)
}
} else if !os.IsNotExist(err) {
return fmt.Errorf("error checking for existing file at %q: %w", o.enginePath, err)
}

if _, err := os.Stat(o.cliConfigsPath); err == nil {
return fmt.Errorf("file present at target path for 2.x CLI configs %q", o.cliConfigsPath)
} else if !os.IsNotExist(err) {
return fmt.Errorf("error checking for existing file at %q: %w", o.cliConfigsPath, err)
}

if _, err := os.Stat(o.cqPath); err == nil {
return fmt.Errorf("file present at target path for exported continuous queries %q", o.cqPath)
} else if !os.IsNotExist(err) {
return fmt.Errorf("error checking for existing file at %q: %w", o.cqPath, err)
}

return nil
}

// clearPaths deletes any files already present at the specified V2 output paths.
func (o *optionsV2) clearPaths() error {
danxmoran marked this conversation as resolved.
Show resolved Hide resolved
if o.configPath != "" {
if err := os.RemoveAll(o.configPath); err != nil {
return fmt.Errorf("couldn't delete existing file at %q: %w", o.configPath, err)
}
}

if err := os.RemoveAll(o.boltPath); err != nil {
return fmt.Errorf("couldn't delete existing file at %q: %w", o.boltPath, err)
}

if err := os.RemoveAll(o.enginePath); err != nil {
return fmt.Errorf("couldn't delete existing file at %q: %w", o.enginePath, err)
}

if _, err = os.Stat(targetOpts.cliConfigsPath); err == nil {
return fmt.Errorf("file present at target path for 2.x CLI configs '%s'", targetOpts.cliConfigsPath)
if err := os.RemoveAll(o.cliConfigsPath); err != nil {
return fmt.Errorf("couldn't delete existing file at %q: %w", o.cliConfigsPath, err)
}

if _, err = os.Stat(targetOpts.cqPath); err == nil {
return fmt.Errorf("file present at target path for exported continuous queries '%s'", targetOpts.cqPath)
if err := os.RemoveAll(o.cqPath); err != nil {
return fmt.Errorf("couldn't delete existing file at %q: %w", o.cqPath, err)
}

return nil
Expand Down
51 changes: 47 additions & 4 deletions cmd/influxd/upgrade/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,24 @@ func TestPathValidations(t *testing.T) {
enginePath: enginePath,
}

err = validatePaths(sourceOpts, targetOpts)
err = sourceOpts.validatePaths()
require.NotNil(t, err, "Must fail")
assert.Contains(t, err.Error(), "1.x DB dir")

err = os.MkdirAll(filepath.Join(v1Dir, "meta"), 0777)
require.Nil(t, err)

err = validatePaths(sourceOpts, targetOpts)
err = sourceOpts.validatePaths()
require.NotNil(t, err, "Must fail")
assert.Contains(t, err.Error(), "1.x meta.db")

err = ioutil.WriteFile(filepath.Join(v1Dir, "meta", "meta.db"), []byte{1}, 0777)
require.Nil(t, err)

err = validatePaths(sourceOpts, targetOpts)
err = sourceOpts.validatePaths()
require.Nil(t, err)

err = targetOpts.validatePaths()
require.NotNil(t, err, "Must fail")
assert.Contains(t, err.Error(), "2.x engine")

Expand All @@ -65,11 +68,51 @@ func TestPathValidations(t *testing.T) {
err = ioutil.WriteFile(configsPath, []byte{1}, 0777)
require.Nil(t, err)

err = validatePaths(sourceOpts, targetOpts)
err = targetOpts.validatePaths()
require.NotNil(t, err, "Must fail")
assert.Contains(t, err.Error(), "2.x CLI configs")
}

func TestClearTargetPaths(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
require.NoError(t, err)

defer os.RemoveAll(tmpdir)

v2Dir := filepath.Join(tmpdir, "v2db")
boltPath := filepath.Join(v2Dir, bolt.DefaultFilename)
configsPath := filepath.Join(v2Dir, "configs")
enginePath := filepath.Join(v2Dir, "engine")
cqPath := filepath.Join(v2Dir, "cqs")
configPath := filepath.Join(v2Dir, "config")

err = os.MkdirAll(filepath.Join(enginePath, "db"), 0777)
require.NoError(t, err)
err = ioutil.WriteFile(boltPath, []byte{1}, 0777)
require.NoError(t, err)
err = ioutil.WriteFile(configsPath, []byte{1}, 0777)
require.NoError(t, err)
err = ioutil.WriteFile(cqPath, []byte{1}, 0777)
require.NoError(t, err)
err = ioutil.WriteFile(configPath, []byte{1}, 0777)
require.NoError(t, err)

targetOpts := &optionsV2{
boltPath: boltPath,
cliConfigsPath: configsPath,
enginePath: enginePath,
configPath: configPath,
cqPath: cqPath,
}

err = targetOpts.validatePaths()
require.Error(t, err)
err = targetOpts.clearPaths()
require.NoError(t, err)
err = targetOpts.validatePaths()
require.NoError(t, err)
}

func TestDbURL(t *testing.T) {

type testCase struct {
Expand Down