cmd/uplink: fix migration for some old configs

some old configs had a value like

	access: <data>

in the yaml. this would end up causing migration to
create a json file where it had no access values and
a default name of the data. that's not what the command
expects to operate on, so now we fix that during
migration and add a little mini migration for any
users that may have hit it.

Change-Id: I4c98ca5d09d043fe9338738ef6b4f930f933892c
This commit is contained in:
Jeff Wendling 2022-02-09 16:17:21 -05:00 committed by Fadila
parent 05a17ef42d
commit e2e5882c86
2 changed files with 56 additions and 18 deletions

View File

@ -40,6 +40,15 @@ func (ex *external) loadAccesses() error {
return errs.Wrap(err)
}
// older versions may have written out invalid access mapping files
// so check here and resave if necessary.
defaultName, ok := checkAccessMapping(jsonInput.Default, jsonInput.Accesses)
if ok {
if err := ex.SaveAccessInfo(defaultName, jsonInput.Accesses); err != nil {
return errs.Wrap(err)
}
}
ex.access.defaultName = jsonInput.Default
ex.access.accesses = jsonInput.Accesses
ex.access.loaded = true
@ -47,38 +56,38 @@ func (ex *external) loadAccesses() error {
return nil
}
func parseAccessOrPossiblyFile(serializedOrFile string) (*uplink.Access, error) {
if access, err := uplink.ParseAccess(serializedOrFile); err == nil {
func parseAccessDataOrPossiblyFile(accessDataOrFile string) (*uplink.Access, error) {
if access, err := uplink.ParseAccess(accessDataOrFile); err == nil {
return access, nil
}
serialized, err := ioutil.ReadFile(serializedOrFile)
accessData, err := ioutil.ReadFile(accessDataOrFile)
if err != nil {
return nil, errs.Wrap(err)
}
return uplink.ParseAccess(string(bytes.TrimSpace(serialized)))
return uplink.ParseAccess(string(bytes.TrimSpace(accessData)))
}
func (ex *external) OpenAccess(accessName string) (access *uplink.Access, err error) {
if access, err := parseAccessOrPossiblyFile(accessName); err == nil {
func (ex *external) OpenAccess(accessDesc string) (access *uplink.Access, err error) {
if access, err := parseAccessDataOrPossiblyFile(accessDesc); err == nil {
return access, nil
}
accessDefault, accesses, err := ex.GetAccessInfo(true)
defaultName, accesses, err := ex.GetAccessInfo(true)
if err != nil {
return nil, err
}
if accessName != "" {
accessDefault = accessName
if accessDesc != "" {
defaultName = accessDesc
}
if data, ok := accesses[accessDefault]; ok {
return uplink.ParseAccess(data)
if accessData, ok := accesses[defaultName]; ok {
return uplink.ParseAccess(accessData)
}
// the default was likely a name, so return a potentially nicer message.
if len(accessDefault) < 20 {
return nil, errs.New("Cannot find access named %q in saved accesses", accessDefault)
if len(defaultName) < 20 {
return nil, errs.New("Cannot find access named %q in saved accesses", defaultName)
}
return nil, errs.New("Unable to get access grant")
}

View File

@ -4,6 +4,7 @@
package main
import (
"fmt"
"io"
"os"
"strings"
@ -39,11 +40,15 @@ func (ex *external) migrate() (err error) {
// load the information necessary to write the new config from
// the old file.
access, accesses, entries, err := ex.parseLegacyConfig(legacyFh)
defaultName, accesses, entries, err := ex.parseLegacyConfig(legacyFh)
if err != nil {
return errs.Wrap(err)
}
// ensure the loaded config matches the new format where the default
// name is actually a name that always points into the accesses map.
defaultName, _ = checkAccessMapping(defaultName, accesses)
// ensure the directory that will hold the config files exists.
if err := os.MkdirAll(ex.dirs.current, 0755); err != nil {
return errs.Wrap(err)
@ -51,7 +56,7 @@ func (ex *external) migrate() (err error) {
// first, create and write the access file. that way, if there's an error
// creating the config file, we will recreate this file.
if err := ex.SaveAccessInfo(access, accesses); err != nil {
if err := ex.SaveAccessInfo(defaultName, accesses); err != nil {
return errs.Wrap(err)
}
@ -67,7 +72,7 @@ func (ex *external) migrate() (err error) {
// parseLegacyConfig loads the default access name, the map of available accesses, and
// a list of config entries from the yaml file in the reader.
func (ex *external) parseLegacyConfig(r io.Reader) (string, map[string]string, []ini.Entry, error) {
access := ""
defaultName := ""
accesses := make(map[string]string)
entries := make([]ini.Entry, 0)
@ -106,7 +111,7 @@ func (ex *external) parseLegacyConfig(r io.Reader) (string, map[string]string, [
// because they go into a separate file now. check for keys that match
// one of those and stuff them away outside of entries.
if key == "access" {
access = value
defaultName = value
} else if strings.HasPrefix(key, "accesses.") {
accesses[key[len("accesses."):]] = value
} else if section == "accesses" {
@ -142,5 +147,29 @@ func (ex *external) parseLegacyConfig(r io.Reader) (string, map[string]string, [
return "", nil, nil, err
}
return access, accesses, entries, nil
return defaultName, accesses, entries, nil
}
func checkAccessMapping(accessName string, accesses map[string]string) (newName string, ok bool) {
if _, ok := accesses[accessName]; ok {
return accessName, false
}
// the only reason the name would not be present is because
// it's actually an access grant. we could check that, but
// if an error happens, the old config must be broken in
// a way we can't repair, anyway, so let's just keep it the
// same amount of broken. so, all we need to do is pick a
// name that doesn't yet exist.
newName = "main"
for i := 2; ; i++ {
if _, ok := accesses[newName]; !ok {
break
}
newName = fmt.Sprintf("main-%d", i)
}
accesses[newName] = accessName
return newName, true
}