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

algocfg: Add print option to algocfg. #5824

Merged
merged 7 commits into from
Nov 13, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 19 additions & 1 deletion cmd/algocfg/profileCommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
rootCmd.AddCommand(profileCmd)
profileCmd.AddCommand(setProfileCmd)
setProfileCmd.Flags().BoolVarP(&forceUpdate, "yes", "y", false, "Force updates to be written")
profileCmd.AddCommand(printProfileCmd)
profileCmd.AddCommand(listProfileCmd)
}

Expand Down Expand Up @@ -133,6 +134,23 @@
},
}

var printProfileCmd = &cobra.Command{
Use: "print",
Short: "Print config.json to stdout.",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
cfg, err := getConfigForArg(args[0])
if err != nil {
reportErrorf("%v", err)

Check warning on line 144 in cmd/algocfg/profileCommand.go

View check run for this annotation

Codecov / codecov/patch

cmd/algocfg/profileCommand.go#L141-L144

Added lines #L141 - L144 were not covered by tests
}
err = codecs.WriteNonDefaultValues(os.Stdout, cfg, config.GetDefaultLocal(), nil)
if err != nil {
reportErrorf("Error writing config file to stdout: %s", err)

Check warning on line 148 in cmd/algocfg/profileCommand.go

View check run for this annotation

Codecov / codecov/patch

cmd/algocfg/profileCommand.go#L146-L148

Added lines #L146 - L148 were not covered by tests
}
fmt.Fprintf(os.Stdout, "\n")

Check warning on line 150 in cmd/algocfg/profileCommand.go

View check run for this annotation

Codecov / codecov/patch

cmd/algocfg/profileCommand.go#L150

Added line #L150 was not covered by tests
},
}

var setProfileCmd = &cobra.Command{
Use: "set",
Short: "Set config.json file from a profile.",
Expand All @@ -157,7 +175,7 @@
return
}
}
err = codecs.SaveNonDefaultValuesToFile(file, cfg, config.GetDefaultLocal(), nil, true)
err = codecs.SaveNonDefaultValuesToFile(file, cfg, config.GetDefaultLocal(), nil)

Check warning on line 178 in cmd/algocfg/profileCommand.go

View check run for this annotation

Codecov / codecov/patch

cmd/algocfg/profileCommand.go#L178

Added line #L178 was not covered by tests
if err != nil {
reportErrorf("Error saving updated config file '%s' - %s", file, err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/algocfg/resetCommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
}

file := filepath.Join(dataDir, config.ConfigFilename)
err = codecs.SaveNonDefaultValuesToFile(file, cfg, defaults, nil, true)
err = codecs.SaveNonDefaultValuesToFile(file, cfg, defaults, nil)

Check warning on line 66 in cmd/algocfg/resetCommand.go

View check run for this annotation

Codecov / codecov/patch

cmd/algocfg/resetCommand.go#L66

Added line #L66 was not covered by tests
if err != nil {
reportWarnf("Error saving updated config file '%s' - %s", file, err)
anyError = true
Expand Down
2 changes: 1 addition & 1 deletion cmd/algocfg/setCommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
}

file := filepath.Join(dataDir, config.ConfigFilename)
err = codecs.SaveNonDefaultValuesToFile(file, cfg, config.GetDefaultLocal(), nil, true)
err = codecs.SaveNonDefaultValuesToFile(file, cfg, config.GetDefaultLocal(), nil)

Check warning on line 69 in cmd/algocfg/setCommand.go

View check run for this annotation

Codecov / codecov/patch

cmd/algocfg/setCommand.go#L69

Added line #L69 was not covered by tests
if err != nil {
reportWarnf("Error saving updated config file '%s' - %s", file, err)
anyError = true
Expand Down
2 changes: 1 addition & 1 deletion config/localTemplate.go
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ func (cfg Local) SaveAllToDisk(root string) error {
func (cfg Local) SaveToFile(filename string) error {
var alwaysInclude []string
alwaysInclude = append(alwaysInclude, "Version")
return codecs.SaveNonDefaultValuesToFile(filename, cfg, defaultLocal, alwaysInclude, true)
return codecs.SaveNonDefaultValuesToFile(filename, cfg, defaultLocal, alwaysInclude)
}

// DNSSecuritySRVEnforced returns true if SRV response verification enforced
Expand Down
67 changes: 34 additions & 33 deletions util/codecs/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -48,55 +49,44 @@
return
}

func writeBytes(writer io.Writer, object interface{}, prettyFormat bool) error {
var enc *json.Encoder
if prettyFormat {
enc = NewFormattedJSONEncoder(writer)
} else {
enc = json.NewEncoder(writer)
}
return enc.Encode(object)
}

// SaveObjectToFile implements the common pattern for saving an object to a file as json
func SaveObjectToFile(filename string, object interface{}, prettyFormat bool) error {
gmalouf marked this conversation as resolved.
Show resolved Hide resolved
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
var enc *json.Encoder
if prettyFormat {
enc = NewFormattedJSONEncoder(f)
} else {
enc = json.NewEncoder(f)
}
err = enc.Encode(object)
return err
return writeBytes(f, object, prettyFormat)
}

// SaveNonDefaultValuesToFile saves an object to a file as json, but only fields that are not
// WriteNonDefaultValues writes object to a writer as json, but only fields that are not
// currently set to be the default value.
// Optionally, you can specify an array of field names to always include.
func SaveNonDefaultValuesToFile(filename string, object, defaultObject interface{}, ignore []string, prettyFormat bool) error {
winder marked this conversation as resolved.
Show resolved Hide resolved
// Serialize object to temporary file.
// Read file into string array
func WriteNonDefaultValues(writer io.Writer, object, defaultObject interface{}, ignore []string) error {
// Iterate one line at a time, parse Name
// If ignore contains Name, don't delete
// Use reflection to compare object[Name].value == defaultObject[Name].value
// If same, delete line from array
// When done, ensure last value line doesn't include comma
// Write string array to file.

file, err := os.CreateTemp("", "encsndv")
if err != nil {
return err
}
name := file.Name()
file.Close()
winder marked this conversation as resolved.
Show resolved Hide resolved

defer os.Remove(name)
// Save object to file pretty-formatted so we can read one value-per-line
err = SaveObjectToFile(name, object, true)
var buf bytes.Buffer
err := writeBytes(&buf, object, true)
if err != nil {
return err
}
content := buf.Bytes()

// Read lines from encoded file into string array
content, err := os.ReadFile(name)
if err != nil {
return err
}
valueLines := strings.Split(string(content), "\n")

// Create maps of the name->value pairs for the object and the defaults
Expand Down Expand Up @@ -155,19 +145,30 @@
}
}

combined := strings.Join(newFile, "\n")
combined = strings.TrimRight(combined, "\r\n ")
_, err = writer.Write([]byte(combined))
return err
}

// SaveNonDefaultValuesToFile saves an object to a file as json, but only fields that are not
// currently set to be the default value.
// Optionally, you can specify an array of field names to always include.
func SaveNonDefaultValuesToFile(filename string, object, defaultObject interface{}, ignore []string) error {

Check warning on line 157 in util/codecs/json.go

View check run for this annotation

Codecov / codecov/patch

util/codecs/json.go#L157

Added line #L157 was not covered by tests
gmalouf marked this conversation as resolved.
Show resolved Hide resolved
outFile, err := os.Create(filename)
if err != nil {
return err
}
defer outFile.Close()
writer := bufio.NewWriter(outFile)
combined := strings.Join(newFile, "\n")
combined = strings.TrimRight(combined, "\r\n ")
_, err = writer.WriteString(combined)
if err == nil {
writer.Flush()

err = WriteNonDefaultValues(writer, object, defaultObject, ignore)
if err != nil {
return err

Check warning on line 167 in util/codecs/json.go

View check run for this annotation

Codecov / codecov/patch

util/codecs/json.go#L165-L167

Added lines #L165 - L167 were not covered by tests
}
return err

writer.Flush()
return nil

Check warning on line 171 in util/codecs/json.go

View check run for this annotation

Codecov / codecov/patch

util/codecs/json.go#L170-L171

Added lines #L170 - L171 were not covered by tests
}

func extractValueName(line string) (name string) {
Expand Down
121 changes: 119 additions & 2 deletions util/codecs/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@
package codecs

import (
"github.com/algorand/go-algorand/test/partitiontest"
"github.com/stretchr/testify/require"
"bytes"
"os"
"path"
"testing"
"time"

"github.com/stretchr/testify/require"

"github.com/algorand/go-algorand/test/partitiontest"
)

type testValue struct {
Expand All @@ -30,6 +36,7 @@ type testValue struct {

func TestIsDefaultValue(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

a := require.New(t)

Expand All @@ -52,3 +59,113 @@ func TestIsDefaultValue(t *testing.T) {
a.False(isDefaultValue("Int", objectValues, defaultValues))
a.True(isDefaultValue("Missing", objectValues, defaultValues))
}

func TestSaveObjectToFile(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

type TestType struct {
A uint64
B string
}

obj := TestType{1024, "test"}

// prettyFormat = false
{
filename := path.Join(t.TempDir(), "test.json")
SaveObjectToFile(filename, obj, false)
data, err := os.ReadFile(filename)
require.NoError(t, err)
expected := `{"A":1024,"B":"test"}
`
require.Equal(t, expected, string(data))
}

// prettyFormat = true
{
filename := path.Join(t.TempDir(), "test.json")
SaveObjectToFile(filename, obj, true)
data, err := os.ReadFile(filename)
require.NoError(t, err)
expected := `{
"A": 1024,
"B": "test"
}
`
require.Equal(t, expected, string(data))
}

}

func TestWriteNonDefaultValue(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

type TestType struct {
Version uint32
Archival bool
GossipFanout int
NetAddress string
ReconnectTime time.Duration
}

defaultObject := TestType{
Version: 1,
Archival: true,
GossipFanout: 50,
NetAddress: "Denver",
ReconnectTime: 60 * time.Second,
}

testcases := []struct {
name string
in TestType
out string
ignore []string
}{
{
name: "all defaults",
in: defaultObject,
out: `{
}`,
}, {
name: "some defaults",
in: TestType{
Version: 1,
Archival: false,
GossipFanout: 25,
NetAddress: "Denver",
ReconnectTime: 60 * time.Nanosecond,
},
out: `{
"Archival": false,
"GossipFanout": 25,
"ReconnectTime": 60
}`,
}, {
name: "ignore",
in: defaultObject,
ignore: []string{"Version", "Archival", "GossipFanout", "NetAddress", "ReconnectTime"},
out: `{
"Version": 1,
"Archival": true,
"GossipFanout": 50,
"NetAddress": "Denver",
"ReconnectTime": 60000000000
}`,
},
}

for _, tc := range testcases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
a := require.New(t)
var writer bytes.Buffer
err := WriteNonDefaultValues(&writer, tc.in, defaultObject, tc.ignore)
a.NoError(err)
a.Equal(tc.out, writer.String())
})
}
}