Skip to content

Commit

Permalink
Merge pull request #982 from kaleido-io/sharedstorage
Browse files Browse the repository at this point in the history
Misc cleanup for 1.0 branch
  • Loading branch information
awrichar authored Aug 18, 2022
2 parents b039fb3 + 92c2fc1 commit a28c2b3
Show file tree
Hide file tree
Showing 11 changed files with 563 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ lint: ${LINT}
${MOCKERY}:
$(VGO) install github.com/vektra/mockery/cmd/mockery@latest
${LINT}:
$(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
$(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2
ffcommon:
$(eval WSCLIENT_PATH := $(shell $(VGO) list -f '{{.Dir}}' github.com/hyperledger/firefly-common/pkg/wsclient))

Expand Down
25 changes: 25 additions & 0 deletions ffconfig/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# FireFly configuration tool

A tool for managing and migrating config files for Hyperledger FireFly.

## Installation

If you have a local Go development environment, and you have included `${GOPATH}/bin` in your path, you can install with:

```sh
go install github.com/hyperledger/firefly/ffconfig@latest
```

## Usage

### Migration

Parse a config file to find any deprecated items that should be updated to the latest config syntax.
The updated config will be written to stdout (or a file specified with `-o`).
```
ffconfig migrate -f firefly.core.yml [-o new.yml]
```

You may optionally specify `--from` and `--to` versions to run a subset of the migrations.

View the source code for all current migrations at [migrate/migrations.go](migrate/migrations.go).
76 changes: 76 additions & 0 deletions ffconfig/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"fmt"
"io/ioutil"
"os"

"github.com/hyperledger/firefly/ffconfig/migrate"
"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
Use: "ffconfig",
Short: "FireFly configuration tool",
Long: "Tool for managing and migrating config files for Hyperledger FireFly",
RunE: func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("a command is required")
},
}

var migrateCommand = &cobra.Command{
Use: "migrate",
Short: "Migrate a config file to the current version",
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := ioutil.ReadFile(cfgFile)
if err != nil {
return err
}
out, err := migrate.Run(cfg, fromVersion, toVersion)
if err != nil {
return err
}
if outFile == "" {
fmt.Print(string(out))
return nil
}
return ioutil.WriteFile(outFile, out, 0600)
},
}

var cfgFile string
var outFile string
var fromVersion string
var toVersion string

func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "f", "firefly.core.yml", "config file")
migrateCommand.PersistentFlags().StringVarP(&outFile, "out", "o", "", "output file (if unspecified, write to stdout)")
migrateCommand.PersistentFlags().StringVar(&fromVersion, "from", "", "from version (optional, such as 1.0.0)")
migrateCommand.PersistentFlags().StringVar(&toVersion, "to", "", "to version (optional, such as 1.1.0)")
rootCmd.AddCommand(migrateCommand)
}

func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
os.Exit(0)
}
201 changes: 201 additions & 0 deletions ffconfig/migrate/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package migrate

import (
"fmt"
"io"
)

type ConfigItem struct {
value interface{}
parent *ConfigItem
name string
writer io.Writer
}

type ConfigItemIterator struct {
items []*ConfigItem
}

func NewConfigItem(value interface{}, writer io.Writer) *ConfigItem {
return &ConfigItem{value: value, writer: writer}
}

func (c *ConfigItem) hasChild(name string) (exists bool) {
if v, ok := c.value.(map[interface{}]interface{}); ok {
_, exists = v[name]
}
return exists
}

func (c *ConfigItem) deleteChild(name string) {
if v, ok := c.value.(map[interface{}]interface{}); ok {
delete(v, name)
}
}

func (c *ConfigItem) setChild(name string, value interface{}) {
if v, ok := c.value.(map[interface{}]interface{}); ok {
v[name] = value
}
}

func (c *ConfigItem) Get(name string) *ConfigItem {
if v, ok := c.value.(map[interface{}]interface{}); ok {
if child, ok := v[name]; ok {
return &ConfigItem{value: child, parent: c, name: name, writer: c.writer}
}
}
return &ConfigItem{value: nil, parent: c, name: name, writer: c.writer}
}

func (c *ConfigItemIterator) Get(name string) *ConfigItemIterator {
items := make([]*ConfigItem, len(c.items))
for i, item := range c.items {
items[i] = item.Get(name)
}
return &ConfigItemIterator{items: items}
}

func (c *ConfigItem) Path() string {
if c.parent == nil || c.parent.name == "" {
return c.name
}
return c.parent.Path() + "." + c.name
}

func (c *ConfigItem) Exists() bool {
return c.value != nil
}

func (c *ConfigItem) Length() int {
if v, ok := c.value.([]interface{}); ok {
return len(v)
}
return 0
}

func (c *ConfigItem) Each() *ConfigItemIterator {
list, ok := c.value.([]interface{})
if !ok {
return &ConfigItemIterator{items: make([]*ConfigItem, 0)}
}
items := make([]*ConfigItem, len(list))
for i, val := range list {
items[i] = &ConfigItem{
value: val,
parent: c.parent,
name: c.name,
writer: c.writer,
}
}
return &ConfigItemIterator{items: items}
}

func (c *ConfigItemIterator) Run(fn func(item *ConfigItem)) *ConfigItemIterator {
for _, item := range c.items {
fn(item)
}
return c
}

func (c *ConfigItem) Create() *ConfigItem {
if !c.Exists() {
if c.parent != nil {
c.parent.Create()
}
c.value = make(map[interface{}]interface{})
c.parent.setChild(c.name, c.value)
}
return c
}

func (c *ConfigItem) Set(value interface{}) *ConfigItem {
fmt.Fprintf(c.writer, "Create: %s: %s\n", c.Path(), value)
c.value = value
c.parent.Create()
c.parent.setChild(c.name, c.value)
return c
}

func (c *ConfigItemIterator) Set(value interface{}) *ConfigItemIterator {
for _, item := range c.items {
item.Set(value)
}
return c
}

func (c *ConfigItem) SetIfEmpty(value interface{}) *ConfigItem {
if !c.Exists() {
c.Set(value)
}
return c
}

func (c *ConfigItem) Delete() *ConfigItem {
if c.Exists() {
fmt.Fprintf(c.writer, "Delete: %s\n", c.Path())
c.parent.deleteChild(c.name)
}
return c
}

func (c *ConfigItemIterator) Delete() *ConfigItemIterator {
for _, item := range c.items {
item.Delete()
}
return c
}

func (c *ConfigItem) RenameTo(name string) *ConfigItem {
if c.Exists() {
if c.parent.hasChild(name) {
// Don't overwrite if the new key already exists
c.Delete()
} else {
fmt.Fprintf(c.writer, "Rename: %s -> .%s\n", c.Path(), name)
c.parent.deleteChild(c.name)
c.parent.setChild(name, c.value)
c.name = name
}
}
return c
}

func (c *ConfigItemIterator) RenameTo(name string) *ConfigItemIterator {
for _, item := range c.items {
item.RenameTo(name)
}
return c
}

func (c *ConfigItem) ReplaceValue(old interface{}, new interface{}) *ConfigItem {
if c.value == old {
fmt.Fprintf(c.writer, "Change: %s: %s -> %s\n", c.Path(), old, new)
c.value = new
c.parent.setChild(c.name, new)
}
return c
}

func (c *ConfigItemIterator) ReplaceValue(old interface{}, new interface{}) *ConfigItemIterator {
for _, item := range c.items {
item.ReplaceValue(old, new)
}
return c
}
45 changes: 45 additions & 0 deletions ffconfig/migrate/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright © 2022 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package migrate

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestDeleteList(t *testing.T) {
value := map[interface{}]interface{}{
"values": []interface{}{"test1", "test2"},
}
config := &ConfigItem{value: value, writer: os.Stdout}
config.Get("values").Each().Delete()
assert.Equal(t, 0, len(value))
}

func TestNoRename(t *testing.T) {
value := map[interface{}]interface{}{
"key1": "val1",
"key2": "val2",
}
config := &ConfigItem{value: value, writer: os.Stdout}
config.Get("key1").RenameTo("key2")
assert.Equal(t, map[interface{}]interface{}{
"key2": "val2",
}, value)
}
Loading

0 comments on commit a28c2b3

Please sign in to comment.