Skip to content
Open
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
Binary file modified .gitignore
Binary file not shown.
110 changes: 83 additions & 27 deletions tools/configtxlator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"os"
"reflect"
"runtime"
"time"

"github.com/alecthomas/kingpin/v2"
"github.com/cockroachdb/errors"
Expand Down Expand Up @@ -41,30 +42,51 @@ var (
version = metadata.Version
)

// command line flags
// command line flags.
var (
app = kingpin.New("configtxlator", "Utility for generating Hyperledger Fabric channel configurations")

start = app.Command("start", "Start the configtxlator REST server")
hostname = start.Flag("hostname", "The hostname or IP on which the REST server will listen").Default("0.0.0.0").String()
port = start.Flag("port", "The port on which the REST server will listen").Default("7059").Int()
cors = start.Flag("CORS", "Allowable CORS domains, e.g. '*' or 'www.example.com' (may be repeated).").Strings()

protoEncode = app.Command("proto_encode", "Converts a JSON document to protobuf.")
protoEncodeType = protoEncode.Flag("type", "The type of protobuf structure to encode to. For example, 'common.Config'.").Required().String()
hostname = start.Flag(
"hostname",
"The hostname or IP on which the REST server will listen",
).Default("0.0.0.0").String()
port = start.Flag("port", "The port on which the REST server will listen").Default("7059").Int()
cors = start.Flag("CORS", "Allowable CORS domains, e.g. '*' or 'www.example.com' (may be repeated).").Strings()

protoEncode = app.Command("proto_encode", "Converts a JSON document to protobuf.")
protoEncodeType = protoEncode.Flag(
"type",
"The type of protobuf structure to encode to. For example, 'common.Config'.",
).Required().String()
protoEncodeSource = protoEncode.Flag("input", "A file containing the JSON document.").Default(os.Stdin.Name()).File()
protoEncodeDest = protoEncode.Flag("output", "A file to write the output to.").Default(os.Stdout.Name()).OpenFile(os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)

protoDecode = app.Command("proto_decode", "Converts a proto message to JSON.")
protoDecodeType = protoDecode.Flag("type", "The type of protobuf structure to decode from. For example, 'common.Config'.").Required().String()
protoEncodeDest = protoEncode.Flag(
"output",
"A file to write the output to.",
).Default(os.Stdout.Name()).OpenFile(os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)

protoDecode = app.Command("proto_decode", "Converts a proto message to JSON.")
protoDecodeType = protoDecode.Flag(
"type",
"The type of protobuf structure to decode from. For example, 'common.Config'.",
).Required().String()
protoDecodeSource = protoDecode.Flag("input", "A file containing the proto message.").Default(os.Stdin.Name()).File()
protoDecodeDest = protoDecode.Flag("output", "A file to write the JSON document to.").Default(os.Stdout.Name()).OpenFile(os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
protoDecodeDest = protoDecode.Flag(
"output",
"A file to write the JSON document to.",
).Default(os.Stdout.Name()).OpenFile(os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)

computeUpdate = app.Command("compute_update", "Takes two marshaled common.Config messages and computes the config update which transitions between the two.")
computeUpdateOriginal = computeUpdate.Flag("original", "The original config message.").File()
computeUpdateUpdated = computeUpdate.Flag("updated", "The updated config message.").File()
computeUpdateChannelID = computeUpdate.Flag("channel_id", "The name of the channel for this update.").Required().String()
computeUpdateDest = computeUpdate.Flag("output", "A file to write the JSON document to.").Default(os.Stdout.Name()).OpenFile(os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
computeUpdateChannelID = computeUpdate.Flag(
"channel_id",
"The name of the channel for this update.",
).Required().String()
computeUpdateDest = computeUpdate.Flag(
"output",
"A file to write the JSON document to.",
).Default(os.Stdout.Name()).OpenFile(os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)

versionCmd = app.Command("version", "Show version information")
)
Expand All @@ -79,23 +101,51 @@ func main() {
startServer(fmt.Sprintf("%s:%d", *hostname, *port), *cors)
// "proto_encode" command
case protoEncode.FullCommand():
defer (*protoEncodeSource).Close()
defer (*protoEncodeDest).Close()
defer func() {
if err := (*protoEncodeSource).Close(); err != nil {
logger.Warnf("error closing protoEncodeSource: %s", err)
}
}()
defer func() {
if err := (*protoEncodeDest).Close(); err != nil {
logger.Warnf("error closing protoEncodeDest: %s", err)
}
}()
err := encodeProto(*protoEncodeType, *protoEncodeSource, *protoEncodeDest)
if err != nil {
app.Fatalf("Error decoding: %s", err)
}
case protoDecode.FullCommand():
defer (*protoDecodeSource).Close()
defer (*protoDecodeDest).Close()
defer func() {
if err := (*protoDecodeSource).Close(); err != nil {
logger.Warnf("error closing protoDecodeSource: %s", err)
}
}()
defer func() {
if err := (*protoDecodeDest).Close(); err != nil {
logger.Warnf("error closing protoDecodeDest: %s", err)
}
}()
err := decodeProto(*protoDecodeType, *protoDecodeSource, *protoDecodeDest)
if err != nil {
app.Fatalf("Error decoding: %s", err)
}
case computeUpdate.FullCommand():
defer (*computeUpdateOriginal).Close()
defer (*computeUpdateUpdated).Close()
defer (*computeUpdateDest).Close()
defer func() {
if err := (*computeUpdateOriginal).Close(); err != nil {
logger.Warnf("error closing computeUpdateOriginal: %s", err)
}
}()
defer func() {
if err := (*computeUpdateUpdated).Close(); err != nil {
logger.Warnf("error closing computeUpdateUpdated: %s", err)
}
}()
defer func() {
if err := (*computeUpdateDest).Close(); err != nil {
logger.Warnf("error closing computeUpdateDest: %s", err)
}
}()
err := computeUpdt(*computeUpdateOriginal, *computeUpdateUpdated, *computeUpdateDest, *computeUpdateChannelID)
if err != nil {
app.Fatalf("Error computing update: %s", err)
Expand All @@ -114,19 +164,24 @@ func startServer(address string, cors []string) {
app.Fatalf("Could not bind to address '%s': %s", address, err)
}

server := &http.Server{
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
}

if len(cors) > 0 {
origins := handlers.AllowedOrigins(cors)
// Note, configtxlator only exposes POST APIs for the time being, this
// list will need to be expanded if new non-POST APIs are added
methods := handlers.AllowedMethods([]string{http.MethodPost})
headers := handlers.AllowedHeaders([]string{"Content-Type"})
logger.Infof("Serving HTTP requests on %s with CORS %v", listener.Addr(), cors)
err = http.Serve(listener, handlers.CORS(origins, methods, headers)(rest.NewRouter()))
server.Handler = handlers.CORS(origins, methods, headers)(rest.NewRouter())
} else {
logger.Infof("Serving HTTP requests on %s", listener.Addr())
err = http.Serve(listener, rest.NewRouter())
server.Handler = rest.NewRouter()
}

err = server.Serve(listener)
app.Fatalf("Error starting server:[%s]\n", err)
}

Expand Down Expand Up @@ -223,11 +278,12 @@ func computeUpdt(original, updated, output *os.File, channelID string) error {
return errors.Wrapf(err, "error computing config update")
}

cu.ChannelId = channelID

if cu == nil {
return errors.New("error marshaling computed config update: proto: Marshal called with nil")
}

cu.ChannelId = channelID

outBytes, err := proto.Marshal(cu)
if err != nil {
return errors.Wrapf(err, "error marshaling computed config update")
Expand Down
2 changes: 1 addition & 1 deletion tools/cryptogen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var (
version = metadata.Version
)

// command line flags
// command line flags.
var (
app = kingpin.New("cryptogen", "Utility for generating Hyperledger Fabric key material")

Expand Down
16 changes: 9 additions & 7 deletions tools/fxconfig/internal/cli/v1/cliio/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"gopkg.in/yaml.v3"
)

const testValue1 = "value1"

func TestNewCLIPrinter(t *testing.T) {
t.Parallel()

Expand All @@ -34,7 +36,7 @@ func TestCLIPrinter_Print_JSON(t *testing.T) {
printer := NewCLIPrinter(&out, &errOut, FormatJSON)

data := map[string]any{
"key1": "value1",
"key1": testValue1,
"key2": 123,
}

Expand All @@ -43,7 +45,7 @@ func TestCLIPrinter_Print_JSON(t *testing.T) {
var result map[string]any
err := json.Unmarshal(out.Bytes(), &result)
require.NoError(t, err)
require.Equal(t, "value1", result["key1"])
require.Equal(t, testValue1, result["key1"])
require.InEpsilon(t, float64(123), result["key2"], 0.001)
})

Expand Down Expand Up @@ -84,7 +86,7 @@ func TestCLIPrinter_Print_YAML(t *testing.T) {
printer := NewCLIPrinter(&out, &errOut, FormatYAML)

data := map[string]any{
"key1": "value1",
"key1": testValue1,
"key2": 123,
}

Expand All @@ -93,7 +95,7 @@ func TestCLIPrinter_Print_YAML(t *testing.T) {
var result map[string]any
err := yaml.Unmarshal(out.Bytes(), &result)
require.NoError(t, err)
require.Equal(t, "value1", result["key1"])
require.Equal(t, testValue1, result["key1"])
require.Equal(t, 123, result["key2"])
})

Expand Down Expand Up @@ -135,14 +137,14 @@ func TestCLIPrinter_Print_Table(t *testing.T) {
}

data := testStruct{
Field1: "value1",
Field1: testValue1,
Field2: 42,
}

printer.Print(data)

output := out.String()
require.Contains(t, output, "value1")
require.Contains(t, output, testValue1)
require.Contains(t, output, "42")
})
}
Expand Down Expand Up @@ -196,4 +198,4 @@ func TestFormat_Constants(t *testing.T) {
require.Equal(t, FormatTable, Format("table"))
require.Equal(t, FormatJSON, Format("json"))
require.Equal(t, FormatYAML, Format("yaml"))
}
}
14 changes: 8 additions & 6 deletions tools/fxconfig/internal/cli/v1/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import (
"github.com/stretchr/testify/require"
)

const testCmdName = "test"

func TestOutputFlag_Bind(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test"}
cmd := &cobra.Command{Use: testCmdName}
var f outputFlag
f.bind(cmd)

Expand All @@ -28,7 +30,7 @@ func TestOutputFlag_Bind(t *testing.T) {
func TestPolicyFlag_Bind(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test"}
cmd := &cobra.Command{Use: testCmdName}
var f policyFlag
f.bind(cmd)

Expand All @@ -45,7 +47,7 @@ func TestPolicyFlag_Bind(t *testing.T) {
func TestVersionFlag_Bind(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test"}
cmd := &cobra.Command{Use: testCmdName}
var f versionFlag
f.bind(cmd)

Expand All @@ -62,7 +64,7 @@ func TestVersionFlag_Bind(t *testing.T) {
func TestNamespaceDeployFlags_Bind(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test"}
cmd := &cobra.Command{Use: testCmdName}
var f namespaceDeployFlags
f.bind(cmd)

Expand All @@ -80,11 +82,11 @@ func TestNamespaceDeployFlags_Bind(t *testing.T) {
func TestWaitFlag_Bind(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test"}
cmd := &cobra.Command{Use: testCmdName}
var f waitFlag
f.bind(cmd)

flag := cmd.Flags().Lookup("wait")
require.NotNil(t, flag)
require.Equal(t, "false", flag.DefValue)
}
}
10 changes: 5 additions & 5 deletions tools/fxconfig/internal/cli/v1/namespace_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ import (
"github.com/hyperledger/fabric-x/tools/fxconfig/internal/cli/v1/cliio"
)

const testNamespace = "my-namespace"

func TestNewCreateCommand(t *testing.T) {
t.Parallel()

// Execute
cmd := newNsCreateCommand(&CLIContext{App: &testApp{}})

// Assert
require.NotNil(t, cmd, "newNsCreateCommand should return a non-nil command")
require.Equal(t, "create [name]", cmd.Use, "command use should be 'create [name]'")
require.NotEmpty(t, cmd.Short, "command should have a short description")
Expand Down Expand Up @@ -57,7 +57,7 @@ func TestNewCreateCommandRun_TxReturned(t *testing.T) {
cmd.SetOut(&cmdOut)
require.NoError(t, cmd.Flags().Set("policy", "OR('Org1MSP.member')"))

err := cmd.RunE(cmd, []string{"my-namespace"})
err := cmd.RunE(cmd, []string{testNamespace})

require.NoError(t, err)
require.Contains(t, cmdOut.String(), "tx-123")
Expand All @@ -78,7 +78,7 @@ func TestNewCreateCommandRun_NoTx(t *testing.T) {
})
require.NoError(t, cmd.Flags().Set("policy", "OR('Org1MSP.member')"))

err := cmd.RunE(cmd, []string{"my-namespace"})
err := cmd.RunE(cmd, []string{testNamespace})

require.NoError(t, err)
require.Contains(t, printerOut.String(), "Transaction status: STATUS_UNSPECIFIED")
Expand Down Expand Up @@ -140,4 +140,4 @@ func (t *testApp) SubmitTransactionWithWait(
) (app.TxStatus, error) {
args := t.Called(ctx, txID, tx)
return args.Int(0), args.Error(1)
}
}
9 changes: 3 additions & 6 deletions tools/fxconfig/internal/cli/v1/namespace_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,13 @@ import (
func TestNewUpdateCommand(t *testing.T) {
t.Parallel()

// Execute
cmd := newNsUpdateCommand(&CLIContext{App: &testApp{}})

// Assert
require.NotNil(t, cmd, "newNsUpdateCommand should return a non-nil command")
require.Equal(t, "update [name]", cmd.Use, "command use should be 'update [name]'")
require.NotEmpty(t, cmd.Short, "command should have a short description")
require.NotNil(t, cmd.RunE, "command should have a RunE function")

// Verify command-specific required flags
version := cmd.Flag("version")
require.NotNil(t, version, "version flag should exist")

Expand Down Expand Up @@ -58,7 +55,7 @@ func TestNsUpdateCommandRun_TxReturned(t *testing.T) {
require.NoError(t, cmd.Flags().Set("policy", "OR('Org1MSP.member')"))
require.NoError(t, cmd.Flags().Set("version", "1"))

err := cmd.RunE(cmd, []string{"my-namespace"})
err := cmd.RunE(cmd, []string{testNamespace})

require.NoError(t, err)
require.Contains(t, outBuf.String(), "tx-456")
Expand All @@ -79,9 +76,9 @@ func TestNsUpdateCommandRun_NoTx(t *testing.T) {
require.NoError(t, cmd.Flags().Set("policy", "OR('Org1MSP.member')"))
require.NoError(t, cmd.Flags().Set("version", "0"))

err := cmd.RunE(cmd, []string{"my-namespace"})
err := cmd.RunE(cmd, []string{testNamespace})

require.NoError(t, err)
require.Contains(t, printerOut.String(), "Transaction status: STATUS_UNSPECIFIED")
mockApp.AssertExpectations(t)
}
}
Loading