From 880a50b0a86da42f814a64f9558e8f703f2b836d Mon Sep 17 00:00:00 2001 From: Liran Funaro Date: Mon, 27 Mar 2023 13:05:39 +0300 Subject: [PATCH 1/6] CLI Signed-off-by: Liran Funaro --- Makefile | 1 + cli/main.go | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- 3 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 cli/main.go diff --git a/Makefile b/Makefile index 9fe663f..b2e4ad5 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,7 @@ goimports: .PHONY: binary binary: $(GO) build -o $(BIN)/bdb github.com/hyperledger-labs/orion-server/cmd/bdb + $(GO) build -o $(BIN)/orion-cli github.com/hyperledger-labs/orion-sdk-go/cli .PHONY: test test-script: diff --git a/cli/main.go b/cli/main.go new file mode 100644 index 0000000..29627ec --- /dev/null +++ b/cli/main.go @@ -0,0 +1,185 @@ +// Copyright IBM Corp. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + "os" + "runtime/debug" + + "github.com/hyperledger-labs/orion-sdk-go/pkg/bcdb" + "github.com/hyperledger-labs/orion-sdk-go/pkg/config" + orionconfig "github.com/hyperledger-labs/orion-server/config" + "github.com/hyperledger-labs/orion-server/pkg/logger" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func main() { + cmd := OrionCli() + // On failure Cobra prints the usage message and error string, so we only need to exit with a non-0 status + if cmd.Execute() != nil { + os.Exit(1) + } +} + +type CliConnectionConfig struct { + Connection config.ConnectionConfig `yaml:"connection"` + Session config.SessionConfig `yaml:"session"` +} + +func (c *CliConnectionConfig) Read(filePath string) error { + if filePath == "" { + return errors.New("file path is empty") + } + + v := viper.New() + v.SetConfigFile(filePath) + + if err := v.ReadInConfig(); err != nil { + return err + } + + if err := v.UnmarshalExact(c); err != nil { + return err + } + + clientLogger, err := logger.New( + &logger.Config{ + Level: "debug", + OutputPath: []string{"stdout"}, + ErrOutputPath: []string{"stderr"}, + Encoding: "console", + Name: "bcdb-client", + }, + ) + if err != nil { + return err + } + c.Connection.Logger = clientLogger + + return nil +} + +func OrionCli() *cobra.Command { + cmd := &cobra.Command{ + Use: "orion-cli", + Short: "Config orion via CLI.", + } + cmd.AddCommand(versionCmd()) + cmd.AddCommand(configCmd()) + return cmd +} + +func versionCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "version", + Args: cobra.NoArgs, + Short: "Print the version of the cli tool.", + RunE: func(cmd *cobra.Command, args []string) error { + bi, ok := debug.ReadBuildInfo() + if !ok { + return fmt.Errorf("failed to read build info") + } + + cmd.Printf("SDK version: %+v\n", bi.Main.Version) + + for _, dep := range bi.Deps { + if dep.Path == "github.com/hyperledger-labs/orion-server" { + cmd.Printf("Orion server version: %+v\n", dep.Version) + break + } + } + + return nil + }, + } + + return cmd +} + +func abort(tx bcdb.TxContext) { + _ = tx.Abort() +} + +type cliConfigParams struct { + cliConfigPath string + cliConfig CliConnectionConfig + db bcdb.BCDB + session bcdb.DBSession +} + +func configCmd() *cobra.Command { + params := &cliConfigParams{} + cmd := &cobra.Command{ + Use: "config", + Short: "Admin configuration", + RunE: func(cmd *cobra.Command, args []string) error { + var err error + if err = params.cliConfig.Read(params.cliConfigPath); err != nil { + return errors.Wrapf(err, "failed to read CLI configuration file") + } + + params.db, err = bcdb.Create(¶ms.cliConfig.Connection) + if err != nil { + return errors.Wrapf(err, "failed to instanciate a databse connection") + } + + params.session, err = params.db.Session(¶ms.cliConfig.Session) + if err != nil { + return errors.Wrapf(err, "failed to instanciate a databse session") + } + + return nil + }, + } + + cmd.PersistentFlags().StringVarP(¶ms.cliConfigPath, "cli-config-path", "c", "", + "set the absolute path of CLI connection configuration file") + if err := cmd.MarkPersistentFlagRequired("cli-config-path"); err != nil { + panic(err) + } + + cmd.AddCommand(&cobra.Command{ + Use: "get", + Short: "Get server configuration", + RunE: func(cmd *cobra.Command, args []string) error { + tx, err := params.session.ConfigTx() + if err != nil { + return errors.Wrapf(err, "failed to instanciate a config TX") + } + defer abort(tx) + + conf, err := tx.GetClusterConfig() + if err != nil { + return errors.Wrapf(err, "failed to fetch cluster config") + } + + cmd.SilenceUsage = true + cmd.Printf(params.cliConfigPath) + cmd.Printf("%v\n", conf) + + return nil + }, + }) + + cmd.AddCommand(&cobra.Command{ + Use: "set", + Short: "Set server configuration", + RunE: func(cmd *cobra.Command, args []string) error { + + _, err := orionconfig.Read(params.cliConfigPath) + if err != nil { + return err + } + + cmd.SilenceUsage = true + cmd.Printf(params.cliConfigPath) + + return nil + }, + }) + return cmd +} diff --git a/go.mod b/go.mod index 8dce168..5f2b80e 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/golang/protobuf v1.5.2 github.com/hyperledger-labs/orion-server v0.2.10 github.com/pkg/errors v0.9.1 + github.com/spf13/cobra v1.0.0 github.com/spf13/viper v1.10.1 github.com/stretchr/testify v1.7.2 go.uber.org/zap v1.18.1 @@ -56,7 +57,6 @@ require ( github.com/sirupsen/logrus v1.6.0 // indirect github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect - github.com/spf13/cobra v1.0.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.1.1 // indirect From e9cd14e6706d2aac11b441f75c8f9081522667ba Mon Sep 17 00:00:00 2001 From: May Rosenbaum Date: Mon, 12 Jun 2023 10:57:10 +0300 Subject: [PATCH 2/6] orion cli - skeleton and config command Signed-off-by: May Rosenbaum --- Makefile | 2 +- cli/main.go | 321 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 247 insertions(+), 76 deletions(-) diff --git a/Makefile b/Makefile index b2e4ad5..da16b74 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ goimports: .PHONY: binary binary: $(GO) build -o $(BIN)/bdb github.com/hyperledger-labs/orion-server/cmd/bdb - $(GO) build -o $(BIN)/orion-cli github.com/hyperledger-labs/orion-sdk-go/cli + $(GO) build -o $(BIN)/bcdbadmin github.com/hyperledger-labs/orion-sdk-go/cli .PHONY: test test-script: diff --git a/cli/main.go b/cli/main.go index 29627ec..b483ade 100644 --- a/cli/main.go +++ b/cli/main.go @@ -5,7 +5,10 @@ package main import ( "fmt" + "github.com/hyperledger-labs/orion-server/pkg/types" + "io/ioutil" "os" + "path" "runtime/debug" "github.com/hyperledger-labs/orion-sdk-go/pkg/bcdb" @@ -15,64 +18,31 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" + "gopkg.in/yaml.v2" ) func main() { - cmd := OrionCli() + cmd := OrionCliInit() // On failure Cobra prints the usage message and error string, so we only need to exit with a non-0 status if cmd.Execute() != nil { os.Exit(1) } } -type CliConnectionConfig struct { - Connection config.ConnectionConfig `yaml:"connection"` - Session config.SessionConfig `yaml:"session"` -} - -func (c *CliConnectionConfig) Read(filePath string) error { - if filePath == "" { - return errors.New("file path is empty") - } - - v := viper.New() - v.SetConfigFile(filePath) - - if err := v.ReadInConfig(); err != nil { - return err - } - - if err := v.UnmarshalExact(c); err != nil { - return err - } - - clientLogger, err := logger.New( - &logger.Config{ - Level: "debug", - OutputPath: []string{"stdout"}, - ErrOutputPath: []string{"stderr"}, - Encoding: "console", - Name: "bcdb-client", - }, - ) - if err != nil { - return err - } - c.Connection.Logger = clientLogger - - return nil -} - -func OrionCli() *cobra.Command { +func OrionCliInit() *cobra.Command { cmd := &cobra.Command{ - Use: "orion-cli", - Short: "Config orion via CLI.", + Use: "Orion-CLI", + Short: "Config Orion via CLI.", } cmd.AddCommand(versionCmd()) cmd.AddCommand(configCmd()) + cmd.AddCommand(adminCmd()) + cmd.AddCommand(nodeCmd()) + cmd.AddCommand(CAsCmd()) return cmd } +// versionCmd - check what command it is func versionCmd() *cobra.Command { cmd := &cobra.Command{ Use: "version", @@ -100,8 +70,9 @@ func versionCmd() *cobra.Command { return cmd } -func abort(tx bcdb.TxContext) { - _ = tx.Abort() +type CliConnectionConfig struct { + ConnectionConfig config.ConnectionConfig `yaml:"connection"` + SessionConfig config.SessionConfig `yaml:"session"` } type cliConfigParams struct { @@ -111,75 +82,275 @@ type cliConfigParams struct { session bcdb.DBSession } +// config command func configCmd() *cobra.Command { - params := &cliConfigParams{} - cmd := &cobra.Command{ + params := &cliConfigParams{ + cliConfig: CliConnectionConfig{}, + } + + configCmd := &cobra.Command{ Use: "config", - Short: "Admin configuration", + Short: "Admin cluster configuration", + Long: "The config command allows you to manage the cluster configuration. " + + "You can use get to retrieve the current cluster configuration or set to update the cluster configuration", + } + + configCmd.PersistentFlags().StringVarP(¶ms.cliConfigPath, "cli-config-path", "c", "", + "set the absolute path of CLI connection configuration file") + if err := configCmd.MarkPersistentFlagRequired("cli-config-path"); err != nil { + panic(err) + } + + var getServerConfigPath string + getConfigCmd := &cobra.Command{ + Use: "get", + Short: "Get cluster configuration", + Example: "cli config get -c -p ", RunE: func(cmd *cobra.Command, args []string) error { - var err error - if err = params.cliConfig.Read(params.cliConfigPath); err != nil { - return errors.Wrapf(err, "failed to read CLI configuration file") + err := params.CreateDbAndOpenSession() + if err != nil { + return err } - params.db, err = bcdb.Create(¶ms.cliConfig.Connection) + tx, err := params.session.ConfigTx() if err != nil { - return errors.Wrapf(err, "failed to instanciate a databse connection") + return errors.Wrapf(err, "failed to instanciate a config TX") } + defer abort(tx) - params.session, err = params.db.Session(¶ms.cliConfig.Session) + clusterConfig, err := tx.GetClusterConfig() if err != nil { - return errors.Wrapf(err, "failed to instanciate a databse session") + return errors.Wrapf(err, "failed to fetch cluster config") } + configCmd.SilenceUsage = true + + // TODO: parse the cluster config obj to certificate directory and yaml file using WriteClusterConfigToYaml + configCmd.Printf(params.cliConfigPath) + configCmd.Printf("%v\n", clusterConfig) + return nil }, } - cmd.PersistentFlags().StringVarP(¶ms.cliConfigPath, "cli-config-path", "c", "", - "set the absolute path of CLI connection configuration file") - if err := cmd.MarkPersistentFlagRequired("cli-config-path"); err != nil { + getConfigCmd.PersistentFlags().StringVarP(&getServerConfigPath, "server-config-path", "p", "", + "set the absolute path to which the server configuration will be saved") + if err := getConfigCmd.MarkPersistentFlagRequired("server-config-path"); err != nil { panic(err) } - cmd.AddCommand(&cobra.Command{ - Use: "get", - Short: "Get server configuration", + setConfigCmd := &cobra.Command{ + Use: "set", + Short: "Set cluster configuration", RunE: func(cmd *cobra.Command, args []string) error { + + _, err := orionconfig.Read(params.cliConfigPath) + if err != nil { + return err + } + + err = params.CreateDbAndOpenSession() + if err != nil { + return err + } + tx, err := params.session.ConfigTx() if err != nil { return errors.Wrapf(err, "failed to instanciate a config TX") } defer abort(tx) + // TODO: set the cluster configuration + //err := tx.SetClusterConfig() + //if err != nil { + // return errors.Wrapf(err, "failed to fetch cluster config") + //} - conf, err := tx.GetClusterConfig() - if err != nil { - return errors.Wrapf(err, "failed to fetch cluster config") - } + configCmd.SilenceUsage = true + configCmd.Printf(params.cliConfigPath) + + return nil + }, + } - cmd.SilenceUsage = true - cmd.Printf(params.cliConfigPath) - cmd.Printf("%v\n", conf) + configCmd.AddCommand(getConfigCmd, setConfigCmd) + return configCmd +} + +// admin command +func adminCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "admin", + Short: "manage administrators", + Args: nil, + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + } + cmd.AddCommand(&cobra.Command{ + Use: "add", + Short: "Add an admin", + RunE: func(cmd *cobra.Command, args []string) error { return nil }, }) cmd.AddCommand(&cobra.Command{ - Use: "set", - Short: "Set server configuration", + Use: "remove", + Short: "Remove an admin", RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + }) - _, err := orionconfig.Read(params.cliConfigPath) - if err != nil { - return err - } + cmd.AddCommand(&cobra.Command{ + Use: "update", + Short: "Update an admin", + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + }) - cmd.SilenceUsage = true - cmd.Printf(params.cliConfigPath) + return cmd +} +// node command +func nodeCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "node", + Short: "manage cluster", + Args: nil, + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + } + + cmd.AddCommand(&cobra.Command{ + Use: "add", + Short: "Add a cluster node", + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + }) + + cmd.AddCommand(&cobra.Command{ + Use: "remove", + Short: "Remove a cluster node", + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + }) + + cmd.AddCommand(&cobra.Command{ + Use: "update", + Short: "Update a cluster node", + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + }) + + return cmd +} + +// CAs command +func CAsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "CAs", + Short: "manage CA's", + Args: nil, + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + } + + cmd.AddCommand(&cobra.Command{ + Use: "add", + Short: "Add CA", + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + }) + + cmd.AddCommand(&cobra.Command{ + Use: "remove", + Short: "Remove CA", + RunE: func(cmd *cobra.Command, args []string) error { return nil }, }) + return cmd } + +// Read unmarshal the yaml config file into a CliConnectionConfig object. +func (c *CliConnectionConfig) ReadAndConstructCliConnConfig(filePath string) error { + if filePath == "" { + return errors.New("path to the shared configuration file is empty") + } + + v := viper.New() + v.SetConfigFile(filePath) + + if err := v.ReadInConfig(); err != nil { + return errors.Wrapf(err, "error reading shared config file: %s", filePath) + } + + if err := v.UnmarshalExact(c); err != nil { + return errors.Wrapf(err, "unable to unmarshal shared config file: '%s' into struct", filePath) + } + + clientLogger, err := logger.New( + &logger.Config{ + Level: "debug", + OutputPath: []string{"stdout"}, + ErrOutputPath: []string{"stderr"}, + Encoding: "console", + Name: "bcdb-client", + }, + ) + if err != nil { + return err + } + c.ConnectionConfig.Logger = clientLogger + + return nil +} + +// CreateDbAndOpenSession read connection and session configurations to create a db instance and open a session with the server. +func (c *cliConfigParams) CreateDbAndOpenSession() error { + var err error + if err = c.cliConfig.ReadAndConstructCliConnConfig(c.cliConfigPath); err != nil { + return errors.Wrapf(err, "failed to read CLI configuration file") + } + + c.db, err = bcdb.Create(&c.cliConfig.ConnectionConfig) + if err != nil { + return errors.Wrapf(err, "failed to instanciate a databse connection") + } + + c.session, err = c.db.Session(&c.cliConfig.SessionConfig) + if err != nil { + return errors.Wrapf(err, "failed to instanciate a databse session") + } + + return nil +} + +func abort(tx bcdb.TxContext) { + _ = tx.Abort() +} + +// WriteClusterConfigToYaml writes the shared clusterConfig object to a YAML file. +func WriteClusterConfigToYaml(clusterConfig *types.ClusterConfig, configYamlFilePath string) error { + c, err := yaml.Marshal(clusterConfig) + if err != nil { + return err + } + + err = ioutil.WriteFile(path.Join(configYamlFilePath, "config", "config.yml"), c, 0644) + if err != nil { + return err + } + + return nil +} From a26b6432d9f45e5fbde3e0fadc3253ac0e408be8 Mon Sep 17 00:00:00 2001 From: May Rosenbaum Date: Mon, 14 Aug 2023 13:41:57 +0300 Subject: [PATCH 3/6] new cli skeleton - wip Signed-off-by: May Rosenbaum --- cli/commands/admin.go | 43 +++++ cli/commands/admin_test.go | 1 + cli/commands/cas.go | 35 ++++ cli/commands/cas_test.go | 1 + cli/commands/config.go | 190 +++++++++++++++++++ cli/commands/config_test.go | 1 + cli/commands/node.go | 43 +++++ cli/commands/node_test.go | 1 + cli/commands/root.go | 17 ++ cli/commands/version.go | 34 ++++ cli/commands/version_test.go | 1 + cli/main.go | 350 +---------------------------------- go.mod | 15 +- go.sum | 30 +++ 14 files changed, 407 insertions(+), 355 deletions(-) create mode 100644 cli/commands/admin.go create mode 100644 cli/commands/admin_test.go create mode 100644 cli/commands/cas.go create mode 100644 cli/commands/cas_test.go create mode 100644 cli/commands/config.go create mode 100644 cli/commands/config_test.go create mode 100644 cli/commands/node.go create mode 100644 cli/commands/node_test.go create mode 100644 cli/commands/root.go create mode 100644 cli/commands/version.go create mode 100644 cli/commands/version_test.go diff --git a/cli/commands/admin.go b/cli/commands/admin.go new file mode 100644 index 0000000..aec903c --- /dev/null +++ b/cli/commands/admin.go @@ -0,0 +1,43 @@ +package commands + +import ( + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +func AdminCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "admin", + Short: "manage administrators", + Args: nil, + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + } + + cmd.AddCommand(&cobra.Command{ + Use: "add", + Short: "Add an admin", + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + }) + + cmd.AddCommand(&cobra.Command{ + Use: "remove", + Short: "Remove an admin", + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + }) + + cmd.AddCommand(&cobra.Command{ + Use: "update", + Short: "Update an admin", + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + }) + + return cmd +} diff --git a/cli/commands/admin_test.go b/cli/commands/admin_test.go new file mode 100644 index 0000000..cdff10d --- /dev/null +++ b/cli/commands/admin_test.go @@ -0,0 +1 @@ +package commands diff --git a/cli/commands/cas.go b/cli/commands/cas.go new file mode 100644 index 0000000..006ad64 --- /dev/null +++ b/cli/commands/cas.go @@ -0,0 +1,35 @@ +package commands + +import ( + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +func CasCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "CAs", + Short: "manage CA's", + Args: nil, + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + } + + cmd.AddCommand(&cobra.Command{ + Use: "add", + Short: "Add CA", + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + }) + + cmd.AddCommand(&cobra.Command{ + Use: "remove", + Short: "Remove CA", + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + }) + + return cmd +} diff --git a/cli/commands/cas_test.go b/cli/commands/cas_test.go new file mode 100644 index 0000000..cdff10d --- /dev/null +++ b/cli/commands/cas_test.go @@ -0,0 +1 @@ +package commands diff --git a/cli/commands/config.go b/cli/commands/config.go new file mode 100644 index 0000000..14cbc18 --- /dev/null +++ b/cli/commands/config.go @@ -0,0 +1,190 @@ +package commands + +import ( + "github.com/hyperledger-labs/orion-sdk-go/pkg/bcdb" + "github.com/hyperledger-labs/orion-sdk-go/pkg/config" + orionconfig "github.com/hyperledger-labs/orion-server/config" + "github.com/hyperledger-labs/orion-server/pkg/logger" + "github.com/hyperledger-labs/orion-server/pkg/types" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "gopkg.in/yaml.v2" + "io/ioutil" + "path" +) + +type CliConnectionConfig struct { + ConnectionConfig config.ConnectionConfig `yaml:"connection"` + SessionConfig config.SessionConfig `yaml:"session"` +} + +type cliConfigParams struct { + cliConfigPath string + cliConfig CliConnectionConfig + db bcdb.BCDB + session bcdb.DBSession +} + +var params cliConfigParams +var getServerConfigPath string + +func init() { + rootCmd.AddCommand(configCmd) + configCmd.PersistentFlags().StringVarP(¶ms.cliConfigPath, "cli-config-path", "c", "", "set the absolute path of CLI connection configuration file") + configCmd.MarkPersistentFlagRequired("cli-config-path") + + configCmd.AddCommand(getConfigCmd, setConfigCmd) + + getConfigCmd.PersistentFlags().StringVarP(&getServerConfigPath, "server-config-path", "p", "", "set the absolute path to which the server configuration will be saved") + getConfigCmd.MarkPersistentFlagRequired("server-config-path") + + //TODO: add flags to setConfig command if needed +} + +var configCmd = &cobra.Command{ + Use: "config", + Short: "Admin cluster configuration", + Long: "The config command allows you to manage the cluster configuration. " + + "You can use get to retrieve the current cluster configuration or set to update the cluster configuration", +} + +var getConfigCmd = &cobra.Command{ + Use: "get", + Short: "Get cluster configuration", + Example: "cli config get -c -p ", + RunE: getConfig, +} + +var setConfigCmd = &cobra.Command{ + Use: "set", + Short: "Set cluster configuration", + RunE: setConfig, +} + +func getConfig(cmd *cobra.Command, args []string) error { + err := params.CreateDbAndOpenSession() + if err != nil { + return err + } + + tx, err := params.session.ConfigTx() + if err != nil { + return errors.Wrapf(err, "failed to instanciate a config TX") + } + defer abort(tx) + + clusterConfig, err := tx.GetClusterConfig() + if err != nil { + return errors.Wrapf(err, "failed to fetch cluster config") + } + + configCmd.SilenceUsage = true + //TODO: parse the cluster config obj to certificate directory and yaml file using WriteClusterConfigToYaml + configCmd.Printf(params.cliConfigPath) + configCmd.Printf("%v\n", clusterConfig) + + return nil +} + +func setConfig(cmd *cobra.Command, args []string) error { + + _, err := orionconfig.Read(params.cliConfigPath) + if err != nil { + return err + } + + err = params.CreateDbAndOpenSession() + if err != nil { + return err + } + + tx, err := params.session.ConfigTx() + if err != nil { + return errors.Wrapf(err, "failed to instanciate a config TX") + } + defer abort(tx) + // TODO: set the cluster configuration + //err := tx.SetClusterConfig() + //if err != nil { + // return errors.Wrapf(err, "failed to fetch cluster config") + //} + + //configCmd.SilenceUsage = true + //configCmd.Printf(params.cliConfigPath) + + return nil +} + +// Read unmarshal the yaml config file into a CliConnectionConfig object. +func (c *CliConnectionConfig) ReadAndConstructCliConnConfig(filePath string) error { + if filePath == "" { + return errors.New("path to the shared configuration file is empty") + } + + v := viper.New() + v.SetConfigFile(filePath) + + if err := v.ReadInConfig(); err != nil { + return errors.Wrapf(err, "error reading shared config file: %s", filePath) + } + + if err := v.UnmarshalExact(c); err != nil { + return errors.Wrapf(err, "unable to unmarshal shared config file: '%s' into struct", filePath) + } + + clientLogger, err := logger.New( + &logger.Config{ + Level: "debug", + OutputPath: []string{"stdout"}, + ErrOutputPath: []string{"stderr"}, + Encoding: "console", + Name: "bcdb-client", + }, + ) + if err != nil { + return err + } + c.ConnectionConfig.Logger = clientLogger + + return nil +} + +// CreateDbAndOpenSession read connection and session configurations to create a db instance and open a session with the server. +func (c *cliConfigParams) CreateDbAndOpenSession() error { + var err error + if err = c.cliConfig.ReadAndConstructCliConnConfig(c.cliConfigPath); err != nil { + return errors.Wrapf(err, "failed to read CLI configuration file") + } + + c.db, err = bcdb.Create(&c.cliConfig.ConnectionConfig) + if err != nil { + return errors.Wrapf(err, "failed to instanciate a databse connection") + } + + c.session, err = c.db.Session(&c.cliConfig.SessionConfig) + if err != nil { + return errors.Wrapf(err, "failed to instanciate a databse session") + } + + return nil +} + +func abort(tx bcdb.TxContext) { + _ = tx.Abort() +} + +// WriteClusterConfigToYaml writes the shared clusterConfig object to a YAML file. +func WriteClusterConfigToYaml(clusterConfig *types.ClusterConfig, configYamlFilePath string) error { + c, err := yaml.Marshal(clusterConfig) + if err != nil { + return err + } + + err = ioutil.WriteFile(path.Join(configYamlFilePath, "config", "config.yml"), c, 0644) + if err != nil { + return err + } + + return nil +} diff --git a/cli/commands/config_test.go b/cli/commands/config_test.go new file mode 100644 index 0000000..cdff10d --- /dev/null +++ b/cli/commands/config_test.go @@ -0,0 +1 @@ +package commands diff --git a/cli/commands/node.go b/cli/commands/node.go new file mode 100644 index 0000000..7771af0 --- /dev/null +++ b/cli/commands/node.go @@ -0,0 +1,43 @@ +package commands + +import ( + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +func NodeCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "node", + Short: "manage cluster", + Args: nil, + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + } + + cmd.AddCommand(&cobra.Command{ + Use: "add", + Short: "Add a cluster node", + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + }) + + cmd.AddCommand(&cobra.Command{ + Use: "remove", + Short: "Remove a cluster node", + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + }) + + cmd.AddCommand(&cobra.Command{ + Use: "update", + Short: "Update a cluster node", + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, + }) + + return cmd +} diff --git a/cli/commands/node_test.go b/cli/commands/node_test.go new file mode 100644 index 0000000..cdff10d --- /dev/null +++ b/cli/commands/node_test.go @@ -0,0 +1 @@ +package commands diff --git a/cli/commands/root.go b/cli/commands/root.go new file mode 100644 index 0000000..fcdb54b --- /dev/null +++ b/cli/commands/root.go @@ -0,0 +1,17 @@ +package commands + +import ( + "github.com/spf13/cobra" + "os" +) + +var rootCmd = &cobra.Command{ + Use: "bcdbadmin", + Short: "Config Orion via CLI.", +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/cli/commands/version.go b/cli/commands/version.go new file mode 100644 index 0000000..6d609f4 --- /dev/null +++ b/cli/commands/version.go @@ -0,0 +1,34 @@ +package commands + +import ( + "fmt" + "github.com/spf13/cobra" + "runtime/debug" +) + +func init() { + rootCmd.AddCommand(versionCmd) +} + +var versionCmd = &cobra.Command{ + Use: "version", + Args: cobra.NoArgs, + Short: "Print the version of the CLI tool.", + RunE: func(cmd *cobra.Command, args []string) error { + bi, ok := debug.ReadBuildInfo() + if !ok { + return fmt.Errorf("failed to read build info") + } + + cmd.Printf("SDK version: %+v\n", bi.Main.Version) + + for _, dep := range bi.Deps { + if dep.Path == "github.com/hyperledger-labs/orion-server" { + cmd.Printf("Orion server version: %+v\n", dep.Version) + break + } + } + + return nil + }, +} diff --git a/cli/commands/version_test.go b/cli/commands/version_test.go new file mode 100644 index 0000000..cdff10d --- /dev/null +++ b/cli/commands/version_test.go @@ -0,0 +1 @@ +package commands diff --git a/cli/main.go b/cli/main.go index b483ade..897dee0 100644 --- a/cli/main.go +++ b/cli/main.go @@ -3,354 +3,8 @@ package main -import ( - "fmt" - "github.com/hyperledger-labs/orion-server/pkg/types" - "io/ioutil" - "os" - "path" - "runtime/debug" - - "github.com/hyperledger-labs/orion-sdk-go/pkg/bcdb" - "github.com/hyperledger-labs/orion-sdk-go/pkg/config" - orionconfig "github.com/hyperledger-labs/orion-server/config" - "github.com/hyperledger-labs/orion-server/pkg/logger" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "gopkg.in/yaml.v2" -) +import "github.com/hyperledger-labs/orion-sdk-go/cli/commands" func main() { - cmd := OrionCliInit() - // On failure Cobra prints the usage message and error string, so we only need to exit with a non-0 status - if cmd.Execute() != nil { - os.Exit(1) - } -} - -func OrionCliInit() *cobra.Command { - cmd := &cobra.Command{ - Use: "Orion-CLI", - Short: "Config Orion via CLI.", - } - cmd.AddCommand(versionCmd()) - cmd.AddCommand(configCmd()) - cmd.AddCommand(adminCmd()) - cmd.AddCommand(nodeCmd()) - cmd.AddCommand(CAsCmd()) - return cmd -} - -// versionCmd - check what command it is -func versionCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "version", - Args: cobra.NoArgs, - Short: "Print the version of the cli tool.", - RunE: func(cmd *cobra.Command, args []string) error { - bi, ok := debug.ReadBuildInfo() - if !ok { - return fmt.Errorf("failed to read build info") - } - - cmd.Printf("SDK version: %+v\n", bi.Main.Version) - - for _, dep := range bi.Deps { - if dep.Path == "github.com/hyperledger-labs/orion-server" { - cmd.Printf("Orion server version: %+v\n", dep.Version) - break - } - } - - return nil - }, - } - - return cmd -} - -type CliConnectionConfig struct { - ConnectionConfig config.ConnectionConfig `yaml:"connection"` - SessionConfig config.SessionConfig `yaml:"session"` -} - -type cliConfigParams struct { - cliConfigPath string - cliConfig CliConnectionConfig - db bcdb.BCDB - session bcdb.DBSession -} - -// config command -func configCmd() *cobra.Command { - params := &cliConfigParams{ - cliConfig: CliConnectionConfig{}, - } - - configCmd := &cobra.Command{ - Use: "config", - Short: "Admin cluster configuration", - Long: "The config command allows you to manage the cluster configuration. " + - "You can use get to retrieve the current cluster configuration or set to update the cluster configuration", - } - - configCmd.PersistentFlags().StringVarP(¶ms.cliConfigPath, "cli-config-path", "c", "", - "set the absolute path of CLI connection configuration file") - if err := configCmd.MarkPersistentFlagRequired("cli-config-path"); err != nil { - panic(err) - } - - var getServerConfigPath string - getConfigCmd := &cobra.Command{ - Use: "get", - Short: "Get cluster configuration", - Example: "cli config get -c -p ", - RunE: func(cmd *cobra.Command, args []string) error { - err := params.CreateDbAndOpenSession() - if err != nil { - return err - } - - tx, err := params.session.ConfigTx() - if err != nil { - return errors.Wrapf(err, "failed to instanciate a config TX") - } - defer abort(tx) - - clusterConfig, err := tx.GetClusterConfig() - if err != nil { - return errors.Wrapf(err, "failed to fetch cluster config") - } - - configCmd.SilenceUsage = true - - // TODO: parse the cluster config obj to certificate directory and yaml file using WriteClusterConfigToYaml - configCmd.Printf(params.cliConfigPath) - configCmd.Printf("%v\n", clusterConfig) - - return nil - }, - } - - getConfigCmd.PersistentFlags().StringVarP(&getServerConfigPath, "server-config-path", "p", "", - "set the absolute path to which the server configuration will be saved") - if err := getConfigCmd.MarkPersistentFlagRequired("server-config-path"); err != nil { - panic(err) - } - - setConfigCmd := &cobra.Command{ - Use: "set", - Short: "Set cluster configuration", - RunE: func(cmd *cobra.Command, args []string) error { - - _, err := orionconfig.Read(params.cliConfigPath) - if err != nil { - return err - } - - err = params.CreateDbAndOpenSession() - if err != nil { - return err - } - - tx, err := params.session.ConfigTx() - if err != nil { - return errors.Wrapf(err, "failed to instanciate a config TX") - } - defer abort(tx) - // TODO: set the cluster configuration - //err := tx.SetClusterConfig() - //if err != nil { - // return errors.Wrapf(err, "failed to fetch cluster config") - //} - - configCmd.SilenceUsage = true - configCmd.Printf(params.cliConfigPath) - - return nil - }, - } - - configCmd.AddCommand(getConfigCmd, setConfigCmd) - return configCmd -} - -// admin command -func adminCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "admin", - Short: "manage administrators", - Args: nil, - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - } - - cmd.AddCommand(&cobra.Command{ - Use: "add", - Short: "Add an admin", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - }) - - cmd.AddCommand(&cobra.Command{ - Use: "remove", - Short: "Remove an admin", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - }) - - cmd.AddCommand(&cobra.Command{ - Use: "update", - Short: "Update an admin", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - }) - - return cmd -} - -// node command -func nodeCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "node", - Short: "manage cluster", - Args: nil, - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - } - - cmd.AddCommand(&cobra.Command{ - Use: "add", - Short: "Add a cluster node", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - }) - - cmd.AddCommand(&cobra.Command{ - Use: "remove", - Short: "Remove a cluster node", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - }) - - cmd.AddCommand(&cobra.Command{ - Use: "update", - Short: "Update a cluster node", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - }) - - return cmd -} - -// CAs command -func CAsCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "CAs", - Short: "manage CA's", - Args: nil, - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - } - - cmd.AddCommand(&cobra.Command{ - Use: "add", - Short: "Add CA", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - }) - - cmd.AddCommand(&cobra.Command{ - Use: "remove", - Short: "Remove CA", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - }) - - return cmd -} - -// Read unmarshal the yaml config file into a CliConnectionConfig object. -func (c *CliConnectionConfig) ReadAndConstructCliConnConfig(filePath string) error { - if filePath == "" { - return errors.New("path to the shared configuration file is empty") - } - - v := viper.New() - v.SetConfigFile(filePath) - - if err := v.ReadInConfig(); err != nil { - return errors.Wrapf(err, "error reading shared config file: %s", filePath) - } - - if err := v.UnmarshalExact(c); err != nil { - return errors.Wrapf(err, "unable to unmarshal shared config file: '%s' into struct", filePath) - } - - clientLogger, err := logger.New( - &logger.Config{ - Level: "debug", - OutputPath: []string{"stdout"}, - ErrOutputPath: []string{"stderr"}, - Encoding: "console", - Name: "bcdb-client", - }, - ) - if err != nil { - return err - } - c.ConnectionConfig.Logger = clientLogger - - return nil -} - -// CreateDbAndOpenSession read connection and session configurations to create a db instance and open a session with the server. -func (c *cliConfigParams) CreateDbAndOpenSession() error { - var err error - if err = c.cliConfig.ReadAndConstructCliConnConfig(c.cliConfigPath); err != nil { - return errors.Wrapf(err, "failed to read CLI configuration file") - } - - c.db, err = bcdb.Create(&c.cliConfig.ConnectionConfig) - if err != nil { - return errors.Wrapf(err, "failed to instanciate a databse connection") - } - - c.session, err = c.db.Session(&c.cliConfig.SessionConfig) - if err != nil { - return errors.Wrapf(err, "failed to instanciate a databse session") - } - - return nil -} - -func abort(tx bcdb.TxContext) { - _ = tx.Abort() -} - -// WriteClusterConfigToYaml writes the shared clusterConfig object to a YAML file. -func WriteClusterConfigToYaml(clusterConfig *types.ClusterConfig, configYamlFilePath string) error { - c, err := yaml.Marshal(clusterConfig) - if err != nil { - return err - } - - err = ioutil.WriteFile(path.Join(configYamlFilePath, "config", "config.yml"), c, 0644) - if err != nil { - return err - } - - return nil + commands.Execute() } diff --git a/go.mod b/go.mod index 5f2b80e..dd12dba 100644 --- a/go.mod +++ b/go.mod @@ -67,14 +67,15 @@ require ( go.etcd.io/etcd v0.5.0-alpha.5.0.20210226220824-aa7126864d82 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/crypto v0.12.0 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/term v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/tools v0.12.0 // indirect google.golang.org/grpc v1.43.0 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index b38e791..017ccef 100644 --- a/go.sum +++ b/go.sum @@ -454,6 +454,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -463,6 +467,10 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -489,6 +497,10 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -503,6 +515,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -540,10 +554,18 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -552,6 +574,10 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= @@ -575,6 +601,10 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= +golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From a3f4d306aa5f6723a0101343030ebec6bebe6522 Mon Sep 17 00:00:00 2001 From: May Rosenbaum Date: Sun, 3 Sep 2023 17:57:56 +0300 Subject: [PATCH 4/6] temporal with config + test Signed-off-by: May Rosenbaum --- cli/commands/config.go | 238 +++++++++++++++--- cli/commands/config_test.go | 170 +++++++++++++ cli/commands/root.go | 19 +- cli/commands/version.go | 41 ++- cli/main.go | 10 +- .../config-local/bootstrap-shared-config.yaml | 81 ++++++ cli/testdata/config-local/config.yml | 129 ++++++++++ cli/testdata/connection-session-config.yml | 13 + cli/testdata/crypto/CA/CA.key | 5 + cli/testdata/crypto/CA/CA.pem | 12 + cli/testdata/crypto/admin/admin.key | 5 + cli/testdata/crypto/admin/admin.pem | 10 + cli/testdata/crypto/alice/alice.key | 5 + cli/testdata/crypto/alice/alice.pem | 10 + cli/testdata/crypto/bob/bob.key | 5 + cli/testdata/crypto/bob/bob.pem | 10 + cli/testdata/crypto/server/server.key | 5 + cli/testdata/crypto/server/server.pem | 10 + cli/testdata/crypto/user/user.key | 5 + cli/testdata/crypto/user/user.pem | 10 + cli/testdata/tls/CA/CA.key | 5 + cli/testdata/tls/CA/CA.pem | 11 + cli/testdata/tls/CA/CA.srl | 1 + cli/testdata/tls/server/server.csr | 7 + cli/testdata/tls/server/server.key | 5 + cli/testdata/tls/server/server.pem | 9 + deployment/config.yml | 13 + deployment/shared_config.yaml | 81 ++++++ 28 files changed, 863 insertions(+), 62 deletions(-) create mode 100644 cli/testdata/config-local/bootstrap-shared-config.yaml create mode 100644 cli/testdata/config-local/config.yml create mode 100644 cli/testdata/connection-session-config.yml create mode 100644 cli/testdata/crypto/CA/CA.key create mode 100644 cli/testdata/crypto/CA/CA.pem create mode 100644 cli/testdata/crypto/admin/admin.key create mode 100644 cli/testdata/crypto/admin/admin.pem create mode 100644 cli/testdata/crypto/alice/alice.key create mode 100644 cli/testdata/crypto/alice/alice.pem create mode 100644 cli/testdata/crypto/bob/bob.key create mode 100644 cli/testdata/crypto/bob/bob.pem create mode 100644 cli/testdata/crypto/server/server.key create mode 100644 cli/testdata/crypto/server/server.pem create mode 100644 cli/testdata/crypto/user/user.key create mode 100644 cli/testdata/crypto/user/user.pem create mode 100644 cli/testdata/tls/CA/CA.key create mode 100644 cli/testdata/tls/CA/CA.pem create mode 100644 cli/testdata/tls/CA/CA.srl create mode 100644 cli/testdata/tls/server/server.csr create mode 100644 cli/testdata/tls/server/server.key create mode 100644 cli/testdata/tls/server/server.pem create mode 100644 deployment/config.yml create mode 100644 deployment/shared_config.yaml diff --git a/cli/commands/config.go b/cli/commands/config.go index 14cbc18..f3683f3 100644 --- a/cli/commands/config.go +++ b/cli/commands/config.go @@ -1,6 +1,7 @@ package commands import ( + "encoding/pem" "github.com/hyperledger-labs/orion-sdk-go/pkg/bcdb" "github.com/hyperledger-labs/orion-sdk-go/pkg/config" orionconfig "github.com/hyperledger-labs/orion-server/config" @@ -11,7 +12,13 @@ import ( "github.com/spf13/viper" "gopkg.in/yaml.v2" "io/ioutil" + "os" "path" + "strconv" +) + +const ( + ConfigDirName = "configTestRes" ) type CliConnectionConfig struct { @@ -27,39 +34,45 @@ type cliConfigParams struct { } var params cliConfigParams -var getServerConfigPath string +var getClusterConfigPath string + +func configCmd() *cobra.Command { + configCmd := &cobra.Command{ + Use: "config", + Short: "Admin cluster configuration", + Long: "The config command allows you to manage the cluster configuration. " + + "You can use get to retrieve the current cluster configuration or set to update the cluster configuration", + } -func init() { - rootCmd.AddCommand(configCmd) configCmd.PersistentFlags().StringVarP(¶ms.cliConfigPath, "cli-config-path", "c", "", "set the absolute path of CLI connection configuration file") configCmd.MarkPersistentFlagRequired("cli-config-path") - configCmd.AddCommand(getConfigCmd, setConfigCmd) + configCmd.AddCommand(getConfigCmd(), setConfigCmd()) - getConfigCmd.PersistentFlags().StringVarP(&getServerConfigPath, "server-config-path", "p", "", "set the absolute path to which the server configuration will be saved") - getConfigCmd.MarkPersistentFlagRequired("server-config-path") - - //TODO: add flags to setConfig command if needed + return configCmd } -var configCmd = &cobra.Command{ - Use: "config", - Short: "Admin cluster configuration", - Long: "The config command allows you to manage the cluster configuration. " + - "You can use get to retrieve the current cluster configuration or set to update the cluster configuration", -} +func getConfigCmd() *cobra.Command { + getConfigCmd := &cobra.Command{ + Use: "get", + Short: "Get cluster configuration", + Example: "cli config get -c -p ", + RunE: getConfig, + } -var getConfigCmd = &cobra.Command{ - Use: "get", - Short: "Get cluster configuration", - Example: "cli config get -c -p ", - RunE: getConfig, + getConfigCmd.PersistentFlags().StringVarP(&getClusterConfigPath, "cluster-config-path", "p", "", "set the absolute path to which the server configuration will be saved") + getConfigCmd.MarkPersistentFlagRequired("cluster-config-path") + + return getConfigCmd } -var setConfigCmd = &cobra.Command{ - Use: "set", - Short: "Set cluster configuration", - RunE: setConfig, +func setConfigCmd() *cobra.Command { + setConfigCmd := &cobra.Command{ + Use: "set", + Short: "Set cluster configuration", + RunE: setConfig, + } + return setConfigCmd } func getConfig(cmd *cobra.Command, args []string) error { @@ -79,10 +92,17 @@ func getConfig(cmd *cobra.Command, args []string) error { return errors.Wrapf(err, "failed to fetch cluster config") } - configCmd.SilenceUsage = true - //TODO: parse the cluster config obj to certificate directory and yaml file using WriteClusterConfigToYaml - configCmd.Printf(params.cliConfigPath) - configCmd.Printf("%v\n", clusterConfig) + //configCmd.SilenceUsage = true + + err = parseAndSaveCerts(clusterConfig, getClusterConfigPath) + if err != nil { + return errors.Wrapf(err, "failed to fetch certificates from cluster config") + } + + err = WriteClusterConfigToYaml(clusterConfig, getClusterConfigPath) + if err != nil { + return errors.Wrapf(err, "failed to create cluster config yaml file") + } return nil } @@ -116,7 +136,7 @@ func setConfig(cmd *cobra.Command, args []string) error { return nil } -// Read unmarshal the yaml config file into a CliConnectionConfig object. +// ReadAndConstructCliConnConfig read unmarshal the yaml config file into a CliConnectionConfig object func (c *CliConnectionConfig) ReadAndConstructCliConnConfig(filePath string) error { if filePath == "" { return errors.New("path to the shared configuration file is empty") @@ -174,17 +194,173 @@ func abort(tx bcdb.TxContext) { _ = tx.Abort() } -// WriteClusterConfigToYaml writes the shared clusterConfig object to a YAML file. +// WriteClusterConfigToYaml builds the shared clusterConfig object and writes it to a YAML file. func WriteClusterConfigToYaml(clusterConfig *types.ClusterConfig, configYamlFilePath string) error { - c, err := yaml.Marshal(clusterConfig) + sharedConfiguration := buildSharedClusterConfig(clusterConfig, configYamlFilePath) + c, err := yaml.Marshal(sharedConfiguration) if err != nil { return err } - err = ioutil.WriteFile(path.Join(configYamlFilePath, "config", "config.yml"), c, 0644) + err = ioutil.WriteFile(path.Join(configYamlFilePath, ConfigDirName, "shared_cluster_config.yml"), c, 0644) if err != nil { return err } + return nil +} + +// parse certificates and save in a folder structure +func parseAndSaveCerts(clusterConfig *types.ClusterConfig, getClusterConfigPath string) error { + for _, node := range clusterConfig.Nodes { + nodeCert := node.Certificate + nodePemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: nodeCert}) + nodeCertDirPath := path.Join(getClusterConfigPath, ConfigDirName, "nodes") + err := os.MkdirAll(nodeCertDirPath, 0755) + if err != nil { + return err + } + + fileName := node.GetId() + ".pem" + nodeCertFilePath := path.Join(nodeCertDirPath, fileName) + + err = os.WriteFile(nodeCertFilePath, nodePemCert, 0644) + if err != nil { + return err + } + } + + for _, adminNode := range clusterConfig.Admins { + adminNodeCert := adminNode.Certificate + adminNodePemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: adminNodeCert}) + adminNodeCertDirPath := path.Join(getClusterConfigPath, ConfigDirName, "admins") + err := os.MkdirAll(adminNodeCertDirPath, 0755) + if err != nil { + return err + } + + fileName := adminNode.GetId() + ".pem" + nodeCertFilePath := path.Join(adminNodeCertDirPath, fileName) + + err = os.WriteFile(nodeCertFilePath, adminNodePemCert, 0644) + if err != nil { + return err + } + } + + for i, rootCACert := range clusterConfig.CertAuthConfig.Roots { + rootCAPemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCACert}) + rootCACertDirPath := path.Join(getClusterConfigPath, ConfigDirName, "rootCAs") + err := os.MkdirAll(rootCACertDirPath, 0755) + if err != nil { + return err + } + + fileName := "rootCA" + strconv.Itoa(i) + ".pem" + rootCACertFilePath := path.Join(rootCACertDirPath, fileName) + + err = os.WriteFile(rootCACertFilePath, rootCAPemCert, 0644) + if err != nil { + return err + } + } + + for i, intermediateCACert := range clusterConfig.CertAuthConfig.Intermediates { + intermediateCAPemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: intermediateCACert}) + intermediateCACertDirPath := path.Join(getClusterConfigPath, ConfigDirName, "intermediateCAs") + err := os.MkdirAll(intermediateCACertDirPath, 0755) + if err != nil { + return err + } + + fileName := "intermediateCA" + strconv.Itoa(i) + ".pem" + intermediateCACertFilePath := path.Join(intermediateCACertDirPath, fileName) + + err = os.WriteFile(intermediateCACertFilePath, intermediateCAPemCert, 0644) + if err != nil { + return err + } + } return nil } + +// buildSharedClusterConfig builds the shared configuration from a clusterConfig +func buildSharedClusterConfig(clusterConfig *types.ClusterConfig, configYamlFilePath string) *orionconfig.SharedConfiguration { + var nodesSharedConfiguration []*orionconfig.NodeConf + for _, node := range clusterConfig.Nodes { + nodeSharedConfiguration := &orionconfig.NodeConf{ + NodeID: node.GetId(), + Host: node.GetAddress(), + Port: node.GetPort(), + CertificatePath: path.Join(configYamlFilePath, ConfigDirName, "nodes", node.GetId()+".pem"), + } + nodesSharedConfiguration = append(nodesSharedConfiguration, nodeSharedConfiguration) + } + + var membersSharedConfiguration []*orionconfig.PeerConf + for _, member := range clusterConfig.ConsensusConfig.Members { + memberSharedConfiguration := &orionconfig.PeerConf{ + NodeId: member.GetNodeId(), + RaftId: member.GetRaftId(), + PeerHost: member.GetPeerHost(), + PeerPort: member.GetPeerPort(), + } + membersSharedConfiguration = append(membersSharedConfiguration, memberSharedConfiguration) + } + + var observersSharedConfiguration []*orionconfig.PeerConf + for _, observer := range clusterConfig.ConsensusConfig.Observers { + observerSharedConfiguration := &orionconfig.PeerConf{ + NodeId: observer.GetNodeId(), + RaftId: observer.GetRaftId(), + PeerHost: observer.GetPeerHost(), + PeerPort: observer.GetPeerPort(), + } + observersSharedConfiguration = append(observersSharedConfiguration, observerSharedConfiguration) + } + + var rootCACertsPathSharedConfiguration []string + for i, root := range clusterConfig.CertAuthConfig.Roots { + rootCACertPathSharedConfiguration := "[]" + if root != nil { + rootCACertPathSharedConfiguration = path.Join(configYamlFilePath, ConfigDirName, "rootCAs", "rootCA"+strconv.Itoa(i)+".pem") + } + rootCACertsPathSharedConfiguration = append(rootCACertsPathSharedConfiguration, rootCACertPathSharedConfiguration) + } + + var intermediateCACertsPathSharedConfiguration []string + for i, intermediateCA := range clusterConfig.CertAuthConfig.Intermediates { + intermediateCACertPathSharedConfiguration := "[]" + if intermediateCA != nil { + intermediateCACertPathSharedConfiguration = path.Join(configYamlFilePath, ConfigDirName, "intermediateCAs", "intermediateCA"+strconv.Itoa(i)+".pem") + } + intermediateCACertsPathSharedConfiguration = append(intermediateCACertsPathSharedConfiguration, intermediateCACertPathSharedConfiguration) + } + + sharedConfiguration := &orionconfig.SharedConfiguration{ + Nodes: nodesSharedConfiguration, + Consensus: &orionconfig.ConsensusConf{ + Algorithm: clusterConfig.ConsensusConfig.Algorithm, + Members: membersSharedConfiguration, + Observers: observersSharedConfiguration, + RaftConfig: &orionconfig.RaftConf{ + TickInterval: clusterConfig.ConsensusConfig.RaftConfig.TickInterval, + ElectionTicks: clusterConfig.ConsensusConfig.RaftConfig.ElectionTicks, + HeartbeatTicks: clusterConfig.ConsensusConfig.RaftConfig.HeartbeatTicks, + MaxInflightBlocks: clusterConfig.ConsensusConfig.RaftConfig.MaxInflightBlocks, + SnapshotIntervalSize: clusterConfig.ConsensusConfig.RaftConfig.SnapshotIntervalSize, + }, + }, + CAConfig: orionconfig.CAConfiguration{ + RootCACertsPath: rootCACertsPathSharedConfiguration, + IntermediateCACertsPath: intermediateCACertsPathSharedConfiguration, + }, + Admin: orionconfig.AdminConf{ + ID: clusterConfig.Admins[0].Id, + CertificatePath: path.Join(getClusterConfigPath, ConfigDirName, "admins", clusterConfig.Admins[0].Id+".pem"), + }, + Ledger: orionconfig.LedgerConf{StateMerklePatriciaTrieDisabled: clusterConfig.LedgerConfig.StateMerklePatriciaTrieDisabled}, + } + + return sharedConfiguration +} diff --git a/cli/commands/config_test.go b/cli/commands/config_test.go index cdff10d..a828e41 100644 --- a/cli/commands/config_test.go +++ b/cli/commands/config_test.go @@ -1 +1,171 @@ package commands + +import ( + "github.com/hyperledger-labs/orion-server/config" + "github.com/hyperledger-labs/orion-server/pkg/server" + "github.com/pkg/errors" + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + "os" + "path" + "path/filepath" + "testing" + "time" +) + +func TestInvalidFlagsGetConfigCommand(t *testing.T) { + tests := []struct { + name string + args []string + expectedErrMsg string + }{ + { + name: "No Flags", + args: []string{"config", "get"}, + expectedErrMsg: "required flag(s) \"cli-config-path\", \"cluster-config-path\" not set", + }, + { + name: "Missing Cli Connection Config Flag", + args: []string{"config", "get", "-p", "/path/to/cluster-config.yaml"}, + expectedErrMsg: "required flag(s) \"cli-config-path\" not set", + }, + { + name: "Missing Cluster Config Flag", + args: []string{"config", "get", "-c", "/path/to/cli-config.yaml"}, + expectedErrMsg: "required flag(s) \"cluster-config-path\" not set", + }, + { + name: "File path not found", + args: []string{"config", "get", "-c", "/path/to/cli-config.yaml", "-p", "/path/to/cluster-config.yaml"}, + expectedErrMsg: "failed to read CLI configuration file", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rootCmd := InitializeOrionCli() + rootCmd.SetArgs(tt.args) + err := rootCmd.Execute() + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErrMsg) + }) + } +} + +func TestGetConfigCommand(t *testing.T) { + // 1. Create BCDBHTTPServer and start server + testServer, err := SetupTestServer(t) + defer testServer.Stop() + require.NoError(t, err) + StartTestServer(t, testServer) + + // 2. Get cluster config from the server by the CLI GetConfig command + rootCmd := InitializeOrionCli() + pwd, err := os.Getwd() + require.NoError(t, err) + rootCmd.SetArgs([]string{"config", "get", "-c", "../testdata/connection-session-config.yml", "-p", filepath.Join(pwd, "..", "..")}) + err = rootCmd.Execute() + require.NoError(t, err) + + // 3. Check the server response - check expected certs, server config + err = compareFiles("../testdata/crypto/admin/admin.pem", "../../configTestRes/admins/admin.pem") + require.NoError(t, err) + err = compareFiles("../testdata/crypto/server/server.pem", "../../configTestRes/nodes/orion-server1.pem") + require.NoError(t, err) + err = compareFiles("../testdata/crypto/CA/CA.pem", "../../configTestRes/rootCAs/rootCA0.pem") + require.NoError(t, err) + + expectedConfigRes, err := readSharedConfig("../../configTestRes/shared_cluster_config.yml") + if err != nil { + errors.Wrapf(err, "failed to read expected shared configuration") + } + + sharedConfigRes, err := readSharedConfig("../../configTestRes/shared_cluster_config.yml") + if err != nil { + errors.Wrapf(err, "failed to read shared configuration") + } + + require.Equal(t, expectedConfigRes.Nodes[0].NodeID, sharedConfigRes.Nodes[0].NodeID) + require.Equal(t, expectedConfigRes.Nodes[0].Port, sharedConfigRes.Nodes[0].Port) + require.Equal(t, expectedConfigRes.Nodes[0].Host, sharedConfigRes.Nodes[0].Host) +} + +func SetupTestServer(t *testing.T) (*server.BCDBHTTPServer, error) { + serverLocalConfig, err := readLocalConfig(path.Join("../testdata/config-local/config.yml")) + if err != nil { + return nil, errors.Wrap(err, "error while unmarshaling local config") + } + + serverSharedConfig, err := readSharedConfig(serverLocalConfig.Bootstrap.File) + if err != nil { + return nil, errors.Wrap(err, "error while unmarshaling shared config") + } + + serverConfig := &config.Configurations{ + LocalConfig: serverLocalConfig, + SharedConfig: serverSharedConfig, + JoinBlock: nil, + } + + server, err := server.New(serverConfig) + return server, err +} + +func StartTestServer(t *testing.T, s *server.BCDBHTTPServer) { + err := s.Start() + require.NoError(t, err) + require.Eventually(t, func() bool { return s.IsLeader() == nil }, 30*time.Second, 100*time.Millisecond) +} + +func readLocalConfig(localConfigFile string) (*config.LocalConfiguration, error) { + if localConfigFile == "" { + return nil, errors.New("path to the local configuration file is empty") + } + + v := viper.New() + v.SetConfigFile(localConfigFile) + + if err := v.ReadInConfig(); err != nil { + return nil, errors.Wrapf(err, "error reading local config file: %s", localConfigFile) + } + + localConf := &config.LocalConfiguration{} + if err := v.UnmarshalExact(localConf); err != nil { + return nil, errors.Wrapf(err, "unable to unmarshal local config file: '%s' into struct", localConfigFile) + } + return localConf, nil +} + +func readSharedConfig(sharedConfigFile string) (*config.SharedConfiguration, error) { + if sharedConfigFile == "" { + return nil, errors.New("path to the shared configuration file is empty") + } + + v := viper.New() + v.SetConfigFile(sharedConfigFile) + + if err := v.ReadInConfig(); err != nil { + return nil, errors.Wrapf(err, "error reading shared config file: %s", sharedConfigFile) + } + + sharedConf := &config.SharedConfiguration{} + if err := v.UnmarshalExact(sharedConf); err != nil { + return nil, errors.Wrapf(err, "unable to unmarshal shared config file: '%s' into struct", sharedConfigFile) + } + return sharedConf, nil +} + +func compareFiles(filepath1 string, filepath2 string) error { + data1, err := os.ReadFile(filepath1) + if err != nil { + return errors.Wrapf(err, "failed to read file: '%s'", filepath1) + } + data2, err := os.ReadFile(filepath2) + if err != nil { + return errors.Wrapf(err, "failed to read file: '%s'", filepath2) + } + if string(data1) != string(data2) { + return errors.Wrapf(err, "files are different") + } + return nil +} diff --git a/cli/commands/root.go b/cli/commands/root.go index fcdb54b..7ddd240 100644 --- a/cli/commands/root.go +++ b/cli/commands/root.go @@ -2,16 +2,19 @@ package commands import ( "github.com/spf13/cobra" - "os" ) -var rootCmd = &cobra.Command{ - Use: "bcdbadmin", - Short: "Config Orion via CLI.", +func rootCmd() *cobra.Command { + rootCmd := &cobra.Command{ + Use: "bcdbadmin", + Short: "Config Orion via CLI.", + } + return rootCmd } -func Execute() { - if err := rootCmd.Execute(); err != nil { - os.Exit(1) - } +func InitializeOrionCli() *cobra.Command { + cmd := rootCmd() + cmd.AddCommand(VersionCmd()) + cmd.AddCommand(configCmd()) + return cmd } diff --git a/cli/commands/version.go b/cli/commands/version.go index 6d609f4..6df053f 100644 --- a/cli/commands/version.go +++ b/cli/commands/version.go @@ -6,29 +6,28 @@ import ( "runtime/debug" ) -func init() { - rootCmd.AddCommand(versionCmd) -} - -var versionCmd = &cobra.Command{ - Use: "version", - Args: cobra.NoArgs, - Short: "Print the version of the CLI tool.", - RunE: func(cmd *cobra.Command, args []string) error { - bi, ok := debug.ReadBuildInfo() - if !ok { - return fmt.Errorf("failed to read build info") - } +func VersionCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "version", + Args: cobra.NoArgs, + Short: "Print the version of the CLI tool.", + RunE: func(cmd *cobra.Command, args []string) error { + bi, ok := debug.ReadBuildInfo() + if !ok { + return fmt.Errorf("failed to read build info") + } - cmd.Printf("SDK version: %+v\n", bi.Main.Version) + cmd.Printf("SDK version: %+v\n", bi.Main.Version) - for _, dep := range bi.Deps { - if dep.Path == "github.com/hyperledger-labs/orion-server" { - cmd.Printf("Orion server version: %+v\n", dep.Version) - break + for _, dep := range bi.Deps { + if dep.Path == "github.com/hyperledger-labs/orion-server" { + cmd.Printf("Orion server version: %+v\n", dep.Version) + break + } } - } - return nil - }, + return nil + }, + } + return cmd } diff --git a/cli/main.go b/cli/main.go index 897dee0..f3e1859 100644 --- a/cli/main.go +++ b/cli/main.go @@ -3,8 +3,14 @@ package main -import "github.com/hyperledger-labs/orion-sdk-go/cli/commands" +import ( + "github.com/hyperledger-labs/orion-sdk-go/cli/commands" + "os" +) func main() { - commands.Execute() + cmd := commands.InitializeOrionCli() + if err := cmd.Execute(); err != nil { + os.Exit(1) + } } diff --git a/cli/testdata/config-local/bootstrap-shared-config.yaml b/cli/testdata/config-local/bootstrap-shared-config.yaml new file mode 100644 index 0000000..c57c835 --- /dev/null +++ b/cli/testdata/config-local/bootstrap-shared-config.yaml @@ -0,0 +1,81 @@ +# 3node-shared-config-bootstrap.yml +# +# This file contains the the initial configuration that will be converted into the ledger's genesis block and +# loaded into the database when the server starts with an empty ledger and database. +# +# This part of the configuration is replicated and is common to all nodes. +# After the initial bootstrap, this part of the configuration can change only through configuration transactions. + +# nodes carry the identity, endpoint, and certificate of each database node that serves to clients. +# The nodeId correlates the node definition here with the peer definition in the consensus section. +# The host and port are those that are accessible from clients. +# The certificate is the one used to authenticate with clients and sign blocks and transaction/query responses. +nodes: + - nodeId: orion-server1 + host: 127.0.0.1 + port: 6001 + certificatePath: ../crypto/server/server.pem + +# consensus carries the definitions of the clustered consensus algorithm, members, and parameters. +consensus: + # consensus.algorithm denotes the employed consensus + # algorithm. Currently, only raft consensus is supported + algorithm: raft + # members contains the set of servers that take part in consensus. + # The nodeId correlates the peer definition here with the node definition in the nodes section. + # The host and port are those that are accessible from other peers. + members: + - nodeId: orion-server1 + raftId: 1 + peerHost: 127.0.0.1 + peerPort: 7050 + + # observers contains the set of servers that are allowed to communicate + # with consensus.members, and fetch their state. + observers: [] + + # consensus.raftConfig carries the configuration parameters that are specific to + # the etcd/raft library. + raftConfig: + # tickInterval is the time interval between two Node.Tick invocations. + tickInterval: 100ms + + # electionTicks is the number of Node.Tick invocations that must pass + # between elections. That is, if a follower does not receive any + # message from the leader of current term before ElectionTick has + # elapsed, it will become candidate and start an election. + # electionTicks must be greater than heartbeatTicks. + electionTicks: 50 + + # heartbeatTicks is the number of Node.Tick invocations that must + # pass between heartbeats. That is, a leader sends heartbeat + # messages to maintain its leadership every HeartbeatTick ticks. + heartbeatTicks: 5 + + # maxInflightBlocks limits the max number of in-flight blocks (i.e. Raft messages). + maxInflightBlocks: 50 + + # snapshotIntervalSize is the cumulative data size in bytes since last snapshot, + # after which a new snapshot is taken. + snapshotIntervalSize: 1000000000000 + + +# caConfig defines the paths to the x509 certificates of the root and +# intermediate certificate authorities that issued all the certificates used +# for client facing communication, including signing transactions and blocks. +caConfig: + # The paths to root certificates. At least one is required. for example: + # rootCACertsPath: ./testdata/rootcaA.cert, ./testdata/rootcaB.cert + rootCACertsPath: ../crypto/CA/CA.pem + + # The paths to intermediate certificates. Optional. For example: + # intermediateCACertsPath: ./testdata/midcaA.cert, ./testdata/midcaB.cert + intermediateCACertsPath: [] + +# admin contains the name and certificate of the initial database administrator. +admin: + # admin.id denotes the id of the cluster admin + id: admin + # identity.certificatePath denotes the path + # to the x509 certificate of the cluster admin + certificatePath: ../crypto/admin/admin.pem diff --git a/cli/testdata/config-local/config.yml b/cli/testdata/config-local/config.yml new file mode 100644 index 0000000..2fb3b76 --- /dev/null +++ b/cli/testdata/config-local/config.yml @@ -0,0 +1,129 @@ +# The properties of this server. +server: + # The identity and credentials of this server. + identity: + # identity.id denotes the id of the server. + id: orion-server1 + # identity.certificatePath denotes the path to the x509 certificate + # of the node. This certificate and associated key is used in all + # client facing communication, as well as for signing blocks and + # transaction replies. + certificatePath: ../testdata/crypto/server/server.pem + # identity.keyPath denotes the path to the private key of the node. + keyPath: ../testdata/crypto/server/server.key + # The listen address and port of the network interface used for client + # communication. The external address (or host name) of this interface + # must be accessible to clients, and is declared in: + # consensus.members[i].nodeHost + # consensus.members[i].nodePort + # where consensus.members[i].nodeID == node.identity.id + network: + # network.address denotes the listen address + address: 127.0.0.1 + # network.port denotes the listen port + port: 6001 + database: + # database.name denotes the name of the underlying + # database engine + name: leveldb + # database.ledgerDirectory denotes the root path + # where we store all ledger data + ledgerDirectory: ledger + queueLength: + # queueLength.transaction denotes the maximum + # queue length of waiting transactions + transaction: 1000 + # queueLength.transactionBatch denotes the maximum + # queue length of waiting reordered batches of transactions + reorderedTransactionBatch: 100 + # queueLength.block denotes the maximum queue length + # of waiting blocks + block: 100 + # logLevel can be debug, info, warn, err, and panic + logLevel: info + tls: + enabled: false + # Require client certificates / mutual TLS for inbound connections. + clientAuthRequired: false + # X.509 certificate used for TLS server + serverCertificatePath: ../testdata/tls/server/server.cert + # Private key for TLS server + serverKeyPath: ../testdata/tls/server/server.key + caConfig: + # The paths to root certificates. At least one is required. for example: + # rootCACertsPath: ./testdata/rootcaA.cert, ./testdata/rootcaB.cert + rootCACertsPath: ../testdata/tls/CA/rootca.cert + # The paths to intermediate certificates. Optional. For example: + # intermediateCACertsPath: ./testdata/cluster/midcaA.cert, ./testdata/cluster/midcaB.cert + intermediateCACertsPath: ../testdata/tls/CA/midca.cert + +# blockCreation carries block creation parameters. +blockCreation: + # blockCreation.blockSize denotes the maximum allowed size of the block in MB + maxBlockSize: 2 + + # maxTransactionCountPerBlock denotes the maximum allowed number of + # transactions per block + maxTransactionCountPerBlock: 1 + + # blockTimeout denotes the block timeout in milliseconds + blockTimeout: 50ms + +# The replication settings specific to this server. +replication: + # The directory for the Raft WAL (write ahead log). + walDir: "./tmp/etcdraft/wal" + + # The directory for the Raft snapshots. + snapDir: "./tmp/etcdraft/snapshot" + + # The listen address and port for intra-cluster communication. + # The external address (or host name) of this interface + # must be accessible from all other servers (a.k.a. "peers"), + # and is declared in the shared configuration: + # consensus.members[i].peerHost + # consensus.members[i].peerPort + # where consensus.members[i].nodeID == node.identity.id + network: + # The listen address + address: 127.0.0.1 + # The listen port + port: 7050 + + # TLS settings for intra-cluster communication. + tls: + # Require server-side TLS. + enabled: false + # Require client certificates / mutual TLS for inbound connections. + clientAuthRequired: false + # X.509 certificate used for TLS server + serverCertificatePath: ../testdata/cluster/server.cert + # Private key for TLS server + serverKeyPath: ../testdata/cluster/server.key + # X.509 certificate used for creating TLS client connections. + clientCertificatePath: ../testdata/cluster/client.cert + # Private key used for creating TLS client connections. + clientKeyPath: ../testdata/cluster/client.key + # cluster.tls.caConfig defines the paths to the x509 certificates + # of the root and intermediate certificate authorities that issued + # all the certificates used for intra-cluster communication. + caConfig: + # The paths to root certificates. At least one is required. for example: + # rootCACertsPath: ./testdata/rootcaA.cert, ./testdata/rootcaB.cert + rootCACertsPath: ../testdata/cluster/rootca.cert + + # The paths to intermediate certificates. Optional. For example: + # intermediateCACertsPath: ./testdata/cluster/midcaA.cert, ./testdata/cluster/midcaB.cert + intermediateCACertsPath: ../testdata/cluster/midca.cert + + +# bootstrap specifies the method of starting a new node with an empty ledger and database. +bootstrap: + # method specifies how to use the bootstrap file: + # - 'genesis' means to load it as the initial configuration that will be converted into the ledger's genesis block and + # loaded into the database when the server starts with an empty ledger. + # - 'join' means to load it as a temporary configuration that will be used to connect to existing cluster members + # and on-board by fetching the ledger from them, rebuilding the database in the process (not supported yet). + method: genesis + # file contains the initial configuration that will be used to bootstrap the node, as specified by the method, above. + file: ../testdata/config-local/bootstrap-shared-config.yaml diff --git a/cli/testdata/connection-session-config.yml b/cli/testdata/connection-session-config.yml new file mode 100644 index 0000000..2617113 --- /dev/null +++ b/cli/testdata/connection-session-config.yml @@ -0,0 +1,13 @@ +connectionConfig: + replicaSet: + - id: "orion-server1" + endpoint: "http://127.0.0.1:6001" + rootCAs: "../testdata/crypto/CA/CA.pem" + +sessionConfig: + userConfig: + userID: "admin" + certPath: "../testdata/crypto/admin/admin.pem" + privateKeyPath: "../testdata/crypto/admin/admin.key" + txTimeout: 20s + queryTimeout: 10s diff --git a/cli/testdata/crypto/CA/CA.key b/cli/testdata/crypto/CA/CA.key new file mode 100644 index 0000000..8dd95df --- /dev/null +++ b/cli/testdata/crypto/CA/CA.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIGUsDhRKOiFAh6XFUf6/x5U/ASmqTHK48B3SYYH7vS0YoAoGCCqGSM49 +AwEHoUQDQgAExkt0GIeYarv/1rNGFyd5p72C+WDLFXwCOisInkmSc7Tu0zRQA3Di +XNryteFB7j3jghyObj5E2daP8REMvHphHw== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/crypto/CA/CA.pem b/cli/testdata/crypto/CA/CA.pem new file mode 100644 index 0000000..ffd806e --- /dev/null +++ b/cli/testdata/crypto/CA/CA.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrjCCAVOgAwIBAgIUSw7fbkDu+nx97O39JUYHQfBoFngwCgYIKoZIzj0EAwIw +LDELMAkGA1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4X +DTIzMDgxNTExMTAzM1oXDTI4MDgxMzExMTAzM1owLDELMAkGA1UEBhMCSUwxDjAM +BgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAExkt0GIeYarv/1rNGFyd5p72C+WDLFXwCOisInkmSc7Tu0zRQA3DiXNry +teFB7j3jghyObj5E2daP8REMvHphH6NTMFEwHQYDVR0OBBYEFLOBDYmHGI9R3Zjb +AhVCaCyCAyh0MB8GA1UdIwQYMBaAFLOBDYmHGI9R3ZjbAhVCaCyCAyh0MA8GA1Ud +EwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAILCEE8eBycJxkTsu5j0YUuZ +upInvfOD+wo8xePgfcOWAiEAmd3kY7lE9PZW5n11TrkzK3ZzNdqltxQdHk26QWi0 +4Sw= +-----END CERTIFICATE----- diff --git a/cli/testdata/crypto/admin/admin.key b/cli/testdata/crypto/admin/admin.key new file mode 100644 index 0000000..270ac8a --- /dev/null +++ b/cli/testdata/crypto/admin/admin.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAvsUgUBcRggQL11GNeSHHc1uxtw1SS06KJ++8p99xo7oAoGCCqGSM49 +AwEHoUQDQgAE61ICL+iyVjOWbtB+HvKOhrvr7ZVRlzUoijCQ/1YCILNjNuEz/2zO +n71YT7FbmIrw2hkkOWJOWVcwUN9Pq+SxiQ== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/crypto/admin/admin.pem b/cli/testdata/crypto/admin/admin.pem new file mode 100644 index 0000000..6e7130d --- /dev/null +++ b/cli/testdata/crypto/admin/admin.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUTCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbkwCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTAzNloXDTI4MDgxMzExMTAzNlowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +61ICL+iyVjOWbtB+HvKOhrvr7ZVRlzUoijCQ/1YCILNjNuEz/2zOn71YT7FbmIrw +2hkkOWJOWVcwUN9Pq+SxiTAKBggqhkjOPQQDAgNHADBEAiAr+moYKnQRWaMY+oc9 +PRyNo8NvH0KtDGtOHAbeeixAYQIgGAgO+DikDu48tJsrpepiOaQlpecaJxegvPDq +jFbxZgI= +-----END CERTIFICATE----- diff --git a/cli/testdata/crypto/alice/alice.key b/cli/testdata/crypto/alice/alice.key new file mode 100644 index 0000000..0b86dfd --- /dev/null +++ b/cli/testdata/crypto/alice/alice.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEILqn8X+9beiLlaWwJUYpel9RRrVqdrV08TkhwvVPcTjEoAoGCCqGSM49 +AwEHoUQDQgAERPIvzg+/GJxAr4uRwrcZHOMVwMBZCRLBDbFIqAb1ssOHpy4pc4Ou +loy19xbUMYIqmHogj4fxYP+4yYHkCZpB6Q== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/crypto/alice/alice.pem b/cli/testdata/crypto/alice/alice.pem new file mode 100644 index 0000000..be20a06 --- /dev/null +++ b/cli/testdata/crypto/alice/alice.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUTCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbswCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTAzOVoXDTI4MDgxMzExMTAzOVowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +RPIvzg+/GJxAr4uRwrcZHOMVwMBZCRLBDbFIqAb1ssOHpy4pc4Ouloy19xbUMYIq +mHogj4fxYP+4yYHkCZpB6TAKBggqhkjOPQQDAgNHADBEAiB5L1uiSClq23hg7doM +39yS4bF0jrm4RFtwhZThMzk6JwIgXLvsMt2GUqI5BbeIoOhAD8pe36/xo+rQvr5L +NHNxGTA= +-----END CERTIFICATE----- diff --git a/cli/testdata/crypto/bob/bob.key b/cli/testdata/crypto/bob/bob.key new file mode 100644 index 0000000..6738303 --- /dev/null +++ b/cli/testdata/crypto/bob/bob.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIM1bno3o4CGWOF1bCG1yoUzVpXshqm0OhQjLPvpK+Xi7oAoGCCqGSM49 +AwEHoUQDQgAEF5n4V9ssnDF2X9zyTlBYYc8Gk/nLsDLwoWkivE7yvjLFntzwjxd7 +d2eghjg6A5jziZc0pwKPV8uxWB2Lo58pHQ== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/crypto/bob/bob.pem b/cli/testdata/crypto/bob/bob.pem new file mode 100644 index 0000000..fa67e58 --- /dev/null +++ b/cli/testdata/crypto/bob/bob.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUjCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbwwCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTA0MVoXDTI4MDgxMzExMTA0MVowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +F5n4V9ssnDF2X9zyTlBYYc8Gk/nLsDLwoWkivE7yvjLFntzwjxd7d2eghjg6A5jz +iZc0pwKPV8uxWB2Lo58pHTAKBggqhkjOPQQDAgNIADBFAiBIXstNmxSomEW4JbqP +gmLq7xx+8lsOXuPfIt6zfrsObQIhALCFnuuvKgPNQdABIOAWkkUb+4SP9/Ug+5wf +M+RjZEe6 +-----END CERTIFICATE----- diff --git a/cli/testdata/crypto/server/server.key b/cli/testdata/crypto/server/server.key new file mode 100644 index 0000000..bb744e3 --- /dev/null +++ b/cli/testdata/crypto/server/server.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIBP61HSKvKxnUna0avdlC6bXTiDSSRnqLtpB0jDOoLppoAoGCCqGSM49 +AwEHoUQDQgAEGRFEgxhNRRKEZCEOZEafGjUAxaRptFZ2ykyp4CMYzxBkGNFyTTV6 +/bokrlOmt+nINP9yLyzVdkdAZwUgiqjPcg== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/crypto/server/server.pem b/cli/testdata/crypto/server/server.pem new file mode 100644 index 0000000..d3f40eb --- /dev/null +++ b/cli/testdata/crypto/server/server.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUjCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbgwCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTAzNVoXDTI4MDgxMzExMTAzNVowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +GRFEgxhNRRKEZCEOZEafGjUAxaRptFZ2ykyp4CMYzxBkGNFyTTV6/bokrlOmt+nI +NP9yLyzVdkdAZwUgiqjPcjAKBggqhkjOPQQDAgNIADBFAiEAxbS+eeATmcl76zR9 +SZ1/N0NxQstC7naI4VQxQSUWCysCIFx5skwiTZzFtlVRuvhytU/x/iw880MLwa6x +TJgahAQh +-----END CERTIFICATE----- diff --git a/cli/testdata/crypto/user/user.key b/cli/testdata/crypto/user/user.key new file mode 100644 index 0000000..037fb8a --- /dev/null +++ b/cli/testdata/crypto/user/user.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIGp8OxlyV/yl3hcwYhs++KXCtT0M3A70ck66eurCK2QooAoGCCqGSM49 +AwEHoUQDQgAEHyVPgc5EyZna9Sf777lNrRJ6L9+J254hl8YpH6ya+S04DXXFOkSL +iEHCJPWY0KB/1KmWv75YzmHliBSRqflLUg== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/crypto/user/user.pem b/cli/testdata/crypto/user/user.pem new file mode 100644 index 0000000..6042490 --- /dev/null +++ b/cli/testdata/crypto/user/user.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUjCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbowCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTAzOFoXDTI4MDgxMzExMTAzOFowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +HyVPgc5EyZna9Sf777lNrRJ6L9+J254hl8YpH6ya+S04DXXFOkSLiEHCJPWY0KB/ +1KmWv75YzmHliBSRqflLUjAKBggqhkjOPQQDAgNIADBFAiAhS96L6Ieea5hq4XbJ +5/WUFNtbqzFpKgbIyeWRB+uHxQIhAOlJO5Gqr+F8BKZwckBdjEmHo25mqH1UnOZX +nwjXtnrk +-----END CERTIFICATE----- diff --git a/cli/testdata/tls/CA/CA.key b/cli/testdata/tls/CA/CA.key new file mode 100644 index 0000000..262fb7c --- /dev/null +++ b/cli/testdata/tls/CA/CA.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIN5Vy2Gs2Bw111sE0Td8Hpw9rbCvzxNWjPBFAq6nmy1roAoGCCqGSM49 +AwEHoUQDQgAE29LhU1b1zTwGXVd6N1nW0pxLrGg5Q3r1xB+jYh133CiykXXo1C7s +oYkEZNRkFWrYH1/jvZcmzxK7q3RXNNyhRA== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/tls/CA/CA.pem b/cli/testdata/tls/CA/CA.pem new file mode 100644 index 0000000..fd01566 --- /dev/null +++ b/cli/testdata/tls/CA/CA.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBnjCCAUWgAwIBAgIJAJT28BKqcZrzMAoGCCqGSM49BAMCMCwxCzAJBgNVBAYT +AklMMQ4wDAYDVQQIDAVIYWlmYTENMAsGA1UECgwEQkNEQjAeFw0yMjAyMDcxNjQ5 +MTJaFw0yMzAyMDcxNjQ5MTJaMCwxCzAJBgNVBAYTAklMMQ4wDAYDVQQIDAVIYWlm +YTENMAsGA1UECgwEQkNEQjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNvS4VNW +9c08Bl1XejdZ1tKcS6xoOUN69cQfo2Idd9wospF16NQu7KGJBGTUZBVq2B9f472X +Js8Su6t0VzTcoUSjUDBOMB0GA1UdDgQWBBQKVWnVvlvMV9mZ/kWt5g1a21La7zAf +BgNVHSMEGDAWgBQKVWnVvlvMV9mZ/kWt5g1a21La7zAMBgNVHRMEBTADAQH/MAoG +CCqGSM49BAMCA0cAMEQCIFG0DoAXTXgI2GEEnZMDRsPJmeMMu4ElgMwRxL1xTqG5 +AiBQoJ+FI0HO5yXBuyrzogoUknoHC2ZAh7Hfep7EViV/pA== +-----END CERTIFICATE----- diff --git a/cli/testdata/tls/CA/CA.srl b/cli/testdata/tls/CA/CA.srl new file mode 100644 index 0000000..7d506f2 --- /dev/null +++ b/cli/testdata/tls/CA/CA.srl @@ -0,0 +1 @@ +99CF73DFAFACA151 diff --git a/cli/testdata/tls/server/server.csr b/cli/testdata/tls/server/server.csr new file mode 100644 index 0000000..7681810 --- /dev/null +++ b/cli/testdata/tls/server/server.csr @@ -0,0 +1,7 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIHnMIGOAgEAMCwxCzAJBgNVBAYTAklMMQ4wDAYDVQQIDAVIYWlmYTENMAsGA1UE +CgwEQkNEQjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNDteLe+L8n6uNRx9du7 +OnTOfhuiVjCPo6yYRm8jAuECtS88bMh/bRloWGoHF6b61Hk+/CWvLO/BTDOCvkEO +Cx+gADAKBggqhkjOPQQDAgNIADBFAiBHV0qhTaDZi6PvXxjkg6zl6o4NlIb2/it4 +QOHU8YheIgIhAO47QDfr8nQ/bB8TtnbFCD4RWqCo4SGQ12UHYPLehFGM +-----END CERTIFICATE REQUEST----- diff --git a/cli/testdata/tls/server/server.key b/cli/testdata/tls/server/server.key new file mode 100644 index 0000000..a93d6f2 --- /dev/null +++ b/cli/testdata/tls/server/server.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIImhf20xSPzXI15cyxZHKcPBSxwJlfvbgJP6ziRYweVKoAoGCCqGSM49 +AwEHoUQDQgAE0O14t74vyfq41HH127s6dM5+G6JWMI+jrJhGbyMC4QK1LzxsyH9t +GWhYagcXpvrUeT78Ja8s78FMM4K+QQ4LHw== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/tls/server/server.pem b/cli/testdata/tls/server/server.pem new file mode 100644 index 0000000..40d823d --- /dev/null +++ b/cli/testdata/tls/server/server.pem @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBSDCB7gIJAJnPc9+vrKFRMAoGCCqGSM49BAMCMCwxCzAJBgNVBAYTAklMMQ4w +DAYDVQQIDAVIYWlmYTENMAsGA1UECgwEQkNEQjAeFw0yMjAyMDcxNjQ5MTRaFw0y +MzAyMDcxNjQ5MTRaMCwxCzAJBgNVBAYTAklMMQ4wDAYDVQQIDAVIYWlmYTENMAsG +A1UECgwEQkNEQjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNDteLe+L8n6uNRx +9du7OnTOfhuiVjCPo6yYRm8jAuECtS88bMh/bRloWGoHF6b61Hk+/CWvLO/BTDOC +vkEOCx8wCgYIKoZIzj0EAwIDSQAwRgIhAPGQHBeyC21OMXtJXM2jgNCdxIDyMdy1 +TZty5MzvqiXqAiEAkoirLflqBSP9LQiQAeWMuj9bDc/e0U/XACtDvLOUgZM= +-----END CERTIFICATE----- diff --git a/deployment/config.yml b/deployment/config.yml new file mode 100644 index 0000000..927cdc8 --- /dev/null +++ b/deployment/config.yml @@ -0,0 +1,13 @@ +connectionConfig: + replicaSet: + - id: "orion-server1" + endpoint: "http://127.0.0.1:6001" + rootCAs: "../orion-server/deployment/crypto/CA/CA.pem" + +sessionConfig: + userConfig: + userID: "admin" + certPath: "../orion-server/deployment/crypto/admin/admin.pem" + privateKeyPath: "../orion-server/deployment/crypto/admin/admin.key" + txTimeout: 20s + queryTimeout: 10s diff --git a/deployment/shared_config.yaml b/deployment/shared_config.yaml new file mode 100644 index 0000000..cf94845 --- /dev/null +++ b/deployment/shared_config.yaml @@ -0,0 +1,81 @@ +# 3node-shared-config-bootstrap.yml +# +# This file contains the the initial configuration that will be converted into the ledger's genesis block and +# loaded into the database when the server starts with an empty ledger and database. +# +# This part of the configuration is replicated and is common to all nodes. +# After the initial bootstrap, this part of the configuration can change only through configuration transactions. + +# nodes carry the identity, endpoint, and certificate of each database node that serves to clients. +# The nodeId correlates the node definition here with the peer definition in the consensus section. +# The host and port are those that are accessible from clients. +# The certificate is the one used to authenticate with clients and sign blocks and transaction/query responses. +nodes: + - nodeId: orion-server1 + host: 127.0.0.1 + port: 6001 + certificatePath: ./deployment/crypto/server/server.pem + +# consensus carries the definitions of the clustered consensus algorithm, members, and parameters. +consensus: + # consensus.algorithm denotes the employed consensus + # algorithm. Currently, only raft consensus is supported + algorithm: raft + # members contains the set of servers that take part in consensus. + # The nodeId correlates the peer definition here with the node definition in the nodes section. + # The host and port are those that are accessible from other peers. + members: + - nodeId: orion-server1 + raftId: 1 + peerHost: 127.0.0.1 + peerPort: 7050 + + # observers contains the set of servers that are allowed to communicate + # with consensus.members, and fetch their state. + observers: [] + + # consensus.raftConfig carries the configuration parameters that are specific to + # the etcd/raft library. + raftConfig: + # tickInterval is the time interval between two Node.Tick invocations. + tickInterval: 100ms + + # electionTicks is the number of Node.Tick invocations that must pass + # between elections. That is, if a follower does not receive any + # message from the leader of current term before ElectionTick has + # elapsed, it will become candidate and start an election. + # electionTicks must be greater than heartbeatTicks. + electionTicks: 50 + + # heartbeatTicks is the number of Node.Tick invocations that must + # pass between heartbeats. That is, a leader sends heartbeat + # messages to maintain its leadership every HeartbeatTick ticks. + heartbeatTicks: 5 + + # maxInflightBlocks limits the max number of in-flight blocks (i.e. Raft messages). + maxInflightBlocks: 50 + + # snapshotIntervalSize is the cumulative data size in bytes since last snapshot, + # after which a new snapshot is taken. + snapshotIntervalSize: 1000000000000 + + +# caConfig defines the paths to the x509 certificates of the root and +# intermediate certificate authorities that issued all the certificates used +# for client facing communication, including signing transactions and blocks. +caConfig: + # The paths to root certificates. At least one is required. for example: + # rootCACertsPath: ./testdata/rootcaA.cert, ./testdata/rootcaB.cert + rootCACertsPath: ./deployment/crypto/CA/CA.pem + + # The paths to intermediate certificates. Optional. For example: + # intermediateCACertsPath: ./testdata/midcaA.cert, ./testdata/midcaB.cert + intermediateCACertsPath: [] + +# admin contains the name and certificate of the initial database administrator. +admin: + # admin.id denotes the id of the cluster admin + id: admin + # identity.certificatePath denotes the path + # to the x509 certificate of the cluster admin + certificatePath: ./deployment/crypto/admin/admin.pem From 964036554317137028e7865fe2df514ec7bc1020 Mon Sep 17 00:00:00 2001 From: May Rosenbaum Date: Tue, 5 Sep 2023 17:17:37 +0300 Subject: [PATCH 5/6] get config command and version command + tests Signed-off-by: May Rosenbaum --- cli/README.md | 44 +++++++++++++++++++ cli/commands/config.go | 3 -- cli/commands/version_test.go | 19 ++++++++ .../config-local/bootstrap-shared-config.yaml | 6 +-- 4 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 cli/README.md diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 0000000..3e8c237 --- /dev/null +++ b/cli/README.md @@ -0,0 +1,44 @@ +# Config Orion via CLI + +This command-line tool provides you a simple way to config an orion database server. + +## Commands + +Here we list and describe the available commands. +We give a short explanation of their usage and describe the flags for each command. +We provide real-world examples demonstrating how to use the CLI tool for various tasks. + + +### Version Command +This command prints the version of the CLI tool. +1. Run from 'orion-sdk' root folder +2. Run `bin/bcdbadmin version`. This command has no flags. + + + +### Config Command +This command enables to config an orion server or ask for the configuration of an orion server. + +#### Get Config Command +1. Run from 'orion-sdk' root folder +2. For Get Config Run `bin/bcdbadmin config get [args]`. + + Replace `[args]` with flags. + +### +##### Flags +| Flags | Description | +|-----------------------------|-------------------------------------------------------------------| + | `-c, --cli-config-path` | the absolute path of CLI connection configuration file | +| `-p, --cluster-config-path` | the absolute path to which the server configuration will be saved | + +Both flags are necessary flags. If any flag is missing, the cli will raise an error. + +### +##### Example: + +Running +`bin/bcdbadmin config get -c "A/connection-session-config.yaml" -p "A/B/C"` +reads the connection and session details needed for connecting to a server from `A/connection-session-config.yaml` and +sends a config TX. +It creates a directory in `A/B/C` with the respective certificates and a yaml file, named shared_cluster_config.yaml, that includes the cluster configuration. \ No newline at end of file diff --git a/cli/commands/config.go b/cli/commands/config.go index f3683f3..d593719 100644 --- a/cli/commands/config.go +++ b/cli/commands/config.go @@ -92,8 +92,6 @@ func getConfig(cmd *cobra.Command, args []string) error { return errors.Wrapf(err, "failed to fetch cluster config") } - //configCmd.SilenceUsage = true - err = parseAndSaveCerts(clusterConfig, getClusterConfigPath) if err != nil { return errors.Wrapf(err, "failed to fetch certificates from cluster config") @@ -108,7 +106,6 @@ func getConfig(cmd *cobra.Command, args []string) error { } func setConfig(cmd *cobra.Command, args []string) error { - _, err := orionconfig.Read(params.cliConfigPath) if err != nil { return err diff --git a/cli/commands/version_test.go b/cli/commands/version_test.go index cdff10d..b73e29c 100644 --- a/cli/commands/version_test.go +++ b/cli/commands/version_test.go @@ -1 +1,20 @@ package commands + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func TestVersionCommand(t *testing.T) { + // 1. Create BCDBHTTPServer and start server + testServer, err := SetupTestServer(t) + defer testServer.Stop() + require.NoError(t, err) + StartTestServer(t, testServer) + + // 2. Get the version of the CLI by the CLI Version command + rootCmd := InitializeOrionCli() + rootCmd.SetArgs([]string{"version"}) + err = rootCmd.Execute() + require.NoError(t, err) +} diff --git a/cli/testdata/config-local/bootstrap-shared-config.yaml b/cli/testdata/config-local/bootstrap-shared-config.yaml index c57c835..26f8c9d 100644 --- a/cli/testdata/config-local/bootstrap-shared-config.yaml +++ b/cli/testdata/config-local/bootstrap-shared-config.yaml @@ -14,7 +14,7 @@ nodes: - nodeId: orion-server1 host: 127.0.0.1 port: 6001 - certificatePath: ../crypto/server/server.pem + certificatePath: ../testdata/crypto/server/server.pem # consensus carries the definitions of the clustered consensus algorithm, members, and parameters. consensus: @@ -66,7 +66,7 @@ consensus: caConfig: # The paths to root certificates. At least one is required. for example: # rootCACertsPath: ./testdata/rootcaA.cert, ./testdata/rootcaB.cert - rootCACertsPath: ../crypto/CA/CA.pem + rootCACertsPath: ../testdata/crypto/CA/CA.pem # The paths to intermediate certificates. Optional. For example: # intermediateCACertsPath: ./testdata/midcaA.cert, ./testdata/midcaB.cert @@ -78,4 +78,4 @@ admin: id: admin # identity.certificatePath denotes the path # to the x509 certificate of the cluster admin - certificatePath: ../crypto/admin/admin.pem + certificatePath: ../testdata/crypto/admin/admin.pem From 49bf48fa6a9f94a44df82355fdfa1ad3c71f0b4a Mon Sep 17 00:00:00 2001 From: May Rosenbaum Date: Wed, 13 Sep 2023 19:39:03 +0300 Subject: [PATCH 6/6] CLI - config & version commands + tests Signed-off-by: May Rosenbaum --- cli/README.md | 22 ++-- cli/commands/admin.go | 2 +- cli/commands/admin_test.go | 26 +++++ cli/commands/cas.go | 2 +- cli/commands/cas_test.go | 26 +++++ cli/commands/config.go | 85 ++++++--------- cli/commands/config_test.go | 90 ++++++++------- cli/commands/node.go | 2 +- cli/commands/node_test.go | 26 +++++ cli/commands/root.go | 5 +- cli/commands/shared_config.go | 115 ++++++++++++++++++++ cli/commands/version.go | 2 +- cli/commands/version_test.go | 18 ++- cli/main.go | 3 +- cli/testdata/connection-session-config.yml | 4 +- cli/testdata/expected-get/CA/CA.key | 5 + cli/testdata/expected-get/CA/CA.pem | 12 ++ cli/testdata/expected-get/admin/admin.key | 5 + cli/testdata/expected-get/admin/admin.pem | 10 ++ cli/testdata/expected-get/alice/alice.key | 5 + cli/testdata/expected-get/alice/alice.pem | 10 ++ cli/testdata/expected-get/bob/bob.key | 5 + cli/testdata/expected-get/bob/bob.pem | 10 ++ cli/testdata/expected-get/server/server.key | 5 + cli/testdata/expected-get/server/server.pem | 10 ++ cli/testdata/expected-get/user/user.key | 5 + cli/testdata/expected-get/user/user.pem | 10 ++ 27 files changed, 408 insertions(+), 112 deletions(-) create mode 100644 cli/commands/shared_config.go create mode 100644 cli/testdata/expected-get/CA/CA.key create mode 100644 cli/testdata/expected-get/CA/CA.pem create mode 100644 cli/testdata/expected-get/admin/admin.key create mode 100644 cli/testdata/expected-get/admin/admin.pem create mode 100644 cli/testdata/expected-get/alice/alice.key create mode 100644 cli/testdata/expected-get/alice/alice.pem create mode 100644 cli/testdata/expected-get/bob/bob.key create mode 100644 cli/testdata/expected-get/bob/bob.pem create mode 100644 cli/testdata/expected-get/server/server.key create mode 100644 cli/testdata/expected-get/server/server.pem create mode 100644 cli/testdata/expected-get/user/user.key create mode 100644 cli/testdata/expected-get/user/user.pem diff --git a/cli/README.md b/cli/README.md index 3e8c237..6fa65c9 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,6 +1,10 @@ # Config Orion via CLI -This command-line tool provides you a simple way to config an orion database server. +This command-line tool provides a simple way to config an orion database server. + +## Building the tool +1. Run from `orion-sdk` root folder +2. Run `make binary` to create an executable file named bcdbadmin under `bin` directory. ## Commands @@ -11,7 +15,7 @@ We provide real-world examples demonstrating how to use the CLI tool for various ### Version Command This command prints the version of the CLI tool. -1. Run from 'orion-sdk' root folder +1. Run from `orion-sdk` root folder. 2. Run `bin/bcdbadmin version`. This command has no flags. @@ -20,17 +24,17 @@ This command prints the version of the CLI tool. This command enables to config an orion server or ask for the configuration of an orion server. #### Get Config Command -1. Run from 'orion-sdk' root folder +1. Run from 'orion-sdk' root folder. 2. For Get Config Run `bin/bcdbadmin config get [args]`. Replace `[args]` with flags. ### ##### Flags -| Flags | Description | -|-----------------------------|-------------------------------------------------------------------| - | `-c, --cli-config-path` | the absolute path of CLI connection configuration file | -| `-p, --cluster-config-path` | the absolute path to which the server configuration will be saved | +| Flags | Description | +|-----------------------------|---------------------------------------------------------------------| +| `-c, --cli-config-path` | the absolute or relative path of CLI connection configuration file | +| `-p, --cluster-config-path` | the absolute path to which the server configuration will be saved | Both flags are necessary flags. If any flag is missing, the cli will raise an error. @@ -38,7 +42,7 @@ Both flags are necessary flags. If any flag is missing, the cli will raise an er ##### Example: Running -`bin/bcdbadmin config get -c "A/connection-session-config.yaml" -p "A/B/C"` -reads the connection and session details needed for connecting to a server from `A/connection-session-config.yaml` and +`bin/bcdbadmin config get -c "connection-session-config.yaml" -p "A/B/C"` +reads the connection and session details needed for connecting to a server from `connection-session-config.yaml` and sends a config TX. It creates a directory in `A/B/C` with the respective certificates and a yaml file, named shared_cluster_config.yaml, that includes the cluster configuration. \ No newline at end of file diff --git a/cli/commands/admin.go b/cli/commands/admin.go index aec903c..dbc6953 100644 --- a/cli/commands/admin.go +++ b/cli/commands/admin.go @@ -5,7 +5,7 @@ import ( "github.com/spf13/cobra" ) -func AdminCmd() *cobra.Command { +func adminCmd() *cobra.Command { cmd := &cobra.Command{ Use: "admin", Short: "manage administrators", diff --git a/cli/commands/admin_test.go b/cli/commands/admin_test.go index cdff10d..ca7c6a3 100644 --- a/cli/commands/admin_test.go +++ b/cli/commands/admin_test.go @@ -1 +1,27 @@ package commands + +import ( + "github.com/hyperledger-labs/orion-sdk-go/examples/util" + "github.com/stretchr/testify/require" + "io/ioutil" + "os" + "testing" +) + +func TestAdminCommand(t *testing.T) { + // 1. Create crypto material and start server + tempDir, err := ioutil.TempDir(os.TempDir(), "ExampleTest") + require.NoError(t, err) + + testServer, _, _, err := util.SetupTestEnv(t, tempDir, uint32(6003)) + require.NoError(t, err) + defer testServer.Stop() + util.StartTestServer(t, testServer) + + // 2. Check cas command response + rootCmd := InitializeOrionCli() + rootCmd.SetArgs([]string{"admin"}) + err = rootCmd.Execute() + require.Error(t, err) + require.Equal(t, err.Error(), "not implemented yet") +} diff --git a/cli/commands/cas.go b/cli/commands/cas.go index 006ad64..f6f0162 100644 --- a/cli/commands/cas.go +++ b/cli/commands/cas.go @@ -5,7 +5,7 @@ import ( "github.com/spf13/cobra" ) -func CasCmd() *cobra.Command { +func casCmd() *cobra.Command { cmd := &cobra.Command{ Use: "CAs", Short: "manage CA's", diff --git a/cli/commands/cas_test.go b/cli/commands/cas_test.go index cdff10d..91b546d 100644 --- a/cli/commands/cas_test.go +++ b/cli/commands/cas_test.go @@ -1 +1,27 @@ package commands + +import ( + "github.com/hyperledger-labs/orion-sdk-go/examples/util" + "github.com/stretchr/testify/require" + "io/ioutil" + "os" + "testing" +) + +func TestCasCommand(t *testing.T) { + // 1. Create crypto material and start server + tempDir, err := ioutil.TempDir(os.TempDir(), "ExampleTest") + require.NoError(t, err) + + testServer, _, _, err := util.SetupTestEnv(t, tempDir, uint32(6003)) + require.NoError(t, err) + defer testServer.Stop() + util.StartTestServer(t, testServer) + + // 2. Check cas command response + rootCmd := InitializeOrionCli() + rootCmd.SetArgs([]string{"CAs"}) + err = rootCmd.Execute() + require.Error(t, err) + require.Equal(t, err.Error(), "not implemented yet") +} diff --git a/cli/commands/config.go b/cli/commands/config.go index d593719..5df66ff 100644 --- a/cli/commands/config.go +++ b/cli/commands/config.go @@ -2,6 +2,12 @@ package commands import ( "encoding/pem" + "fmt" + "io/ioutil" + "os" + "path" + "strconv" + "github.com/hyperledger-labs/orion-sdk-go/pkg/bcdb" "github.com/hyperledger-labs/orion-sdk-go/pkg/config" orionconfig "github.com/hyperledger-labs/orion-server/config" @@ -11,10 +17,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "gopkg.in/yaml.v2" - "io/ioutil" - "os" - "path" - "strconv" ) const ( @@ -33,8 +35,10 @@ type cliConfigParams struct { session bcdb.DBSession } -var params cliConfigParams -var getClusterConfigPath string +var ( + params cliConfigParams + getClusterConfigPath string +) func configCmd() *cobra.Command { configCmd := &cobra.Command{ @@ -63,6 +67,7 @@ func getConfigCmd() *cobra.Command { getConfigCmd.PersistentFlags().StringVarP(&getClusterConfigPath, "cluster-config-path", "p", "", "set the absolute path to which the server configuration will be saved") getConfigCmd.MarkPersistentFlagRequired("cluster-config-path") + fmt.Println() return getConfigCmd } @@ -70,7 +75,9 @@ func setConfigCmd() *cobra.Command { setConfigCmd := &cobra.Command{ Use: "set", Short: "Set cluster configuration", - RunE: setConfig, + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("not implemented yet") + }, } return setConfigCmd } @@ -105,34 +112,6 @@ func getConfig(cmd *cobra.Command, args []string) error { return nil } -func setConfig(cmd *cobra.Command, args []string) error { - _, err := orionconfig.Read(params.cliConfigPath) - if err != nil { - return err - } - - err = params.CreateDbAndOpenSession() - if err != nil { - return err - } - - tx, err := params.session.ConfigTx() - if err != nil { - return errors.Wrapf(err, "failed to instanciate a config TX") - } - defer abort(tx) - // TODO: set the cluster configuration - //err := tx.SetClusterConfig() - //if err != nil { - // return errors.Wrapf(err, "failed to fetch cluster config") - //} - - //configCmd.SilenceUsage = true - //configCmd.Printf(params.cliConfigPath) - - return nil -} - // ReadAndConstructCliConnConfig read unmarshal the yaml config file into a CliConnectionConfig object func (c *CliConnectionConfig) ReadAndConstructCliConnConfig(filePath string) error { if filePath == "" { @@ -282,10 +261,10 @@ func parseAndSaveCerts(clusterConfig *types.ClusterConfig, getClusterConfigPath } // buildSharedClusterConfig builds the shared configuration from a clusterConfig -func buildSharedClusterConfig(clusterConfig *types.ClusterConfig, configYamlFilePath string) *orionconfig.SharedConfiguration { - var nodesSharedConfiguration []*orionconfig.NodeConf +func buildSharedClusterConfig(clusterConfig *types.ClusterConfig, configYamlFilePath string) *SharedConfiguration { + var nodesSharedConfiguration []*NodeConf for _, node := range clusterConfig.Nodes { - nodeSharedConfiguration := &orionconfig.NodeConf{ + nodeSharedConfiguration := &NodeConf{ NodeID: node.GetId(), Host: node.GetAddress(), Port: node.GetPort(), @@ -294,9 +273,9 @@ func buildSharedClusterConfig(clusterConfig *types.ClusterConfig, configYamlFile nodesSharedConfiguration = append(nodesSharedConfiguration, nodeSharedConfiguration) } - var membersSharedConfiguration []*orionconfig.PeerConf + var membersSharedConfiguration []*PeerConf for _, member := range clusterConfig.ConsensusConfig.Members { - memberSharedConfiguration := &orionconfig.PeerConf{ + memberSharedConfiguration := &PeerConf{ NodeId: member.GetNodeId(), RaftId: member.GetRaftId(), PeerHost: member.GetPeerHost(), @@ -305,9 +284,9 @@ func buildSharedClusterConfig(clusterConfig *types.ClusterConfig, configYamlFile membersSharedConfiguration = append(membersSharedConfiguration, memberSharedConfiguration) } - var observersSharedConfiguration []*orionconfig.PeerConf + var observersSharedConfiguration []*PeerConf for _, observer := range clusterConfig.ConsensusConfig.Observers { - observerSharedConfiguration := &orionconfig.PeerConf{ + observerSharedConfiguration := &PeerConf{ NodeId: observer.GetNodeId(), RaftId: observer.GetRaftId(), PeerHost: observer.GetPeerHost(), @@ -334,13 +313,22 @@ func buildSharedClusterConfig(clusterConfig *types.ClusterConfig, configYamlFile intermediateCACertsPathSharedConfiguration = append(intermediateCACertsPathSharedConfiguration, intermediateCACertPathSharedConfiguration) } - sharedConfiguration := &orionconfig.SharedConfiguration{ + var adminsSharedConfiguration []*AdminConf + for i, admin := range clusterConfig.Admins { + adminSharedConfiguration := &AdminConf{ + ID: admin.GetId(), + CertificatePath: path.Join(getClusterConfigPath, ConfigDirName, "admins", clusterConfig.Admins[i].GetId()+".pem"), + } + adminsSharedConfiguration = append(adminsSharedConfiguration, adminSharedConfiguration) + } + + sharedConfiguration := &SharedConfiguration{ Nodes: nodesSharedConfiguration, - Consensus: &orionconfig.ConsensusConf{ + Consensus: &ConsensusConf{ Algorithm: clusterConfig.ConsensusConfig.Algorithm, Members: membersSharedConfiguration, Observers: observersSharedConfiguration, - RaftConfig: &orionconfig.RaftConf{ + RaftConfig: &RaftConf{ TickInterval: clusterConfig.ConsensusConfig.RaftConfig.TickInterval, ElectionTicks: clusterConfig.ConsensusConfig.RaftConfig.ElectionTicks, HeartbeatTicks: clusterConfig.ConsensusConfig.RaftConfig.HeartbeatTicks, @@ -352,11 +340,8 @@ func buildSharedClusterConfig(clusterConfig *types.ClusterConfig, configYamlFile RootCACertsPath: rootCACertsPathSharedConfiguration, IntermediateCACertsPath: intermediateCACertsPathSharedConfiguration, }, - Admin: orionconfig.AdminConf{ - ID: clusterConfig.Admins[0].Id, - CertificatePath: path.Join(getClusterConfigPath, ConfigDirName, "admins", clusterConfig.Admins[0].Id+".pem"), - }, - Ledger: orionconfig.LedgerConf{StateMerklePatriciaTrieDisabled: clusterConfig.LedgerConfig.StateMerklePatriciaTrieDisabled}, + Admin: adminsSharedConfiguration, + Ledger: LedgerConf{StateMerklePatriciaTrieDisabled: clusterConfig.LedgerConfig.StateMerklePatriciaTrieDisabled}, } return sharedConfiguration diff --git a/cli/commands/config_test.go b/cli/commands/config_test.go index a828e41..30a774f 100644 --- a/cli/commands/config_test.go +++ b/cli/commands/config_test.go @@ -1,16 +1,17 @@ package commands import ( - "github.com/hyperledger-labs/orion-server/config" - "github.com/hyperledger-labs/orion-server/pkg/server" + "github.com/hyperledger-labs/orion-sdk-go/examples/util" "github.com/pkg/errors" "github.com/spf13/viper" "github.com/stretchr/testify/require" + "io/ioutil" + "net/url" "os" "path" "path/filepath" + "strconv" "testing" - "time" ) func TestInvalidFlagsGetConfigCommand(t *testing.T) { @@ -53,71 +54,78 @@ func TestInvalidFlagsGetConfigCommand(t *testing.T) { } func TestGetConfigCommand(t *testing.T) { - // 1. Create BCDBHTTPServer and start server - testServer, err := SetupTestServer(t) - defer testServer.Stop() + // 1. Create crypto material and start server + tempDir, err := ioutil.TempDir(os.TempDir(), "ExampleTest") + require.NoError(t, err) + + testServer, _, _, err := util.SetupTestEnv(t, tempDir, uint32(6003)) require.NoError(t, err) - StartTestServer(t, testServer) + defer testServer.Stop() + util.StartTestServer(t, testServer) // 2. Get cluster config from the server by the CLI GetConfig command rootCmd := InitializeOrionCli() pwd, err := os.Getwd() require.NoError(t, err) - rootCmd.SetArgs([]string{"config", "get", "-c", "../testdata/connection-session-config.yml", "-p", filepath.Join(pwd, "..", "..")}) + testConfigFilePath := path.Join(tempDir, "config.yml") + rootCmd.SetArgs([]string{"config", "get", "-c", testConfigFilePath, "-p", filepath.Join(pwd, "..", "..")}) err = rootCmd.Execute() require.NoError(t, err) - // 3. Check the server response - check expected certs, server config - err = compareFiles("../testdata/crypto/admin/admin.pem", "../../configTestRes/admins/admin.pem") + // 3. Check the server response + // check that certs are equal to the expected certs + createdDirName := "configTestRes" + relativePathForCreatedDirName := path.Join("..", "..", createdDirName) + err = compareFiles(path.Join(tempDir, "crypto", "admin", "admin.pem"), path.Join(relativePathForCreatedDirName, "admins", "admin.pem")) require.NoError(t, err) - err = compareFiles("../testdata/crypto/server/server.pem", "../../configTestRes/nodes/orion-server1.pem") + err = compareFiles(path.Join(tempDir, "crypto", "node", "node.pem"), path.Join(relativePathForCreatedDirName, "nodes", "server1.pem")) require.NoError(t, err) - err = compareFiles("../testdata/crypto/CA/CA.pem", "../../configTestRes/rootCAs/rootCA0.pem") + err = compareFiles(path.Join(tempDir, "crypto", "CA", "CA.pem"), path.Join(relativePathForCreatedDirName, "rootCAs", "rootCA0.pem")) require.NoError(t, err) - expectedConfigRes, err := readSharedConfig("../../configTestRes/shared_cluster_config.yml") + // extract server endpoint and compare to the endpoint received by Get Config command + expectedConfigRes, err := readConnConfig(testConfigFilePath) if err != nil { errors.Wrapf(err, "failed to read expected shared configuration") } - sharedConfigRes, err := readSharedConfig("../../configTestRes/shared_cluster_config.yml") + actualSharedConfigRes, err := readSharedConfigYaml(path.Join(relativePathForCreatedDirName, "shared_cluster_config.yml")) if err != nil { errors.Wrapf(err, "failed to read shared configuration") } - require.Equal(t, expectedConfigRes.Nodes[0].NodeID, sharedConfigRes.Nodes[0].NodeID) - require.Equal(t, expectedConfigRes.Nodes[0].Port, sharedConfigRes.Nodes[0].Port) - require.Equal(t, expectedConfigRes.Nodes[0].Host, sharedConfigRes.Nodes[0].Host) -} - -func SetupTestServer(t *testing.T) (*server.BCDBHTTPServer, error) { - serverLocalConfig, err := readLocalConfig(path.Join("../testdata/config-local/config.yml")) - if err != nil { - return nil, errors.Wrap(err, "error while unmarshaling local config") - } - - serverSharedConfig, err := readSharedConfig(serverLocalConfig.Bootstrap.File) + require.Equal(t, expectedConfigRes.ConnectionConfig.ReplicaSet[0].ID, actualSharedConfigRes.Nodes[0].NodeID) + url, err := url.Parse(expectedConfigRes.ConnectionConfig.ReplicaSet[0].Endpoint) if err != nil { - return nil, errors.Wrap(err, "error while unmarshaling shared config") + errors.Wrapf(err, "failed to parse server endpoint") } + require.Equal(t, url.Host, actualSharedConfigRes.Nodes[0].Host+":"+strconv.Itoa(int(actualSharedConfigRes.Nodes[0].Port))) - serverConfig := &config.Configurations{ - LocalConfig: serverLocalConfig, - SharedConfig: serverSharedConfig, - JoinBlock: nil, + if err := os.RemoveAll(relativePathForCreatedDirName); err != nil { + errors.Wrapf(err, "failed to cleanup directory: %s", relativePathForCreatedDirName) } - - server, err := server.New(serverConfig) - return server, err } -func StartTestServer(t *testing.T, s *server.BCDBHTTPServer) { - err := s.Start() +func TestSetConfigCommand(t *testing.T) { + // 1. Create crypto material and start server + tempDir, err := ioutil.TempDir(os.TempDir(), "ExampleTest") + require.NoError(t, err) + + testServer, _, _, err := util.SetupTestEnv(t, tempDir, uint32(6003)) require.NoError(t, err) - require.Eventually(t, func() bool { return s.IsLeader() == nil }, 30*time.Second, 100*time.Millisecond) + defer testServer.Stop() + util.StartTestServer(t, testServer) + + // 2. Check cas command response + rootCmd := InitializeOrionCli() + testConfigFilePath := path.Join(tempDir, "config.yml") + rootCmd.SetArgs([]string{"config", "set", "-c", testConfigFilePath}) + err = rootCmd.Execute() + require.Error(t, err) + require.Equal(t, err.Error(), "not implemented yet") } -func readLocalConfig(localConfigFile string) (*config.LocalConfiguration, error) { +func readConnConfig(localConfigFile string) (*CliConnectionConfig, error) { if localConfigFile == "" { return nil, errors.New("path to the local configuration file is empty") } @@ -129,14 +137,14 @@ func readLocalConfig(localConfigFile string) (*config.LocalConfiguration, error) return nil, errors.Wrapf(err, "error reading local config file: %s", localConfigFile) } - localConf := &config.LocalConfiguration{} + localConf := &CliConnectionConfig{} if err := v.UnmarshalExact(localConf); err != nil { return nil, errors.Wrapf(err, "unable to unmarshal local config file: '%s' into struct", localConfigFile) } return localConf, nil } -func readSharedConfig(sharedConfigFile string) (*config.SharedConfiguration, error) { +func readSharedConfigYaml(sharedConfigFile string) (*SharedConfiguration, error) { if sharedConfigFile == "" { return nil, errors.New("path to the shared configuration file is empty") } @@ -148,7 +156,7 @@ func readSharedConfig(sharedConfigFile string) (*config.SharedConfiguration, err return nil, errors.Wrapf(err, "error reading shared config file: %s", sharedConfigFile) } - sharedConf := &config.SharedConfiguration{} + sharedConf := &SharedConfiguration{} if err := v.UnmarshalExact(sharedConf); err != nil { return nil, errors.Wrapf(err, "unable to unmarshal shared config file: '%s' into struct", sharedConfigFile) } diff --git a/cli/commands/node.go b/cli/commands/node.go index 7771af0..4418b65 100644 --- a/cli/commands/node.go +++ b/cli/commands/node.go @@ -5,7 +5,7 @@ import ( "github.com/spf13/cobra" ) -func NodeCmd() *cobra.Command { +func nodeCmd() *cobra.Command { cmd := &cobra.Command{ Use: "node", Short: "manage cluster", diff --git a/cli/commands/node_test.go b/cli/commands/node_test.go index cdff10d..780a7d4 100644 --- a/cli/commands/node_test.go +++ b/cli/commands/node_test.go @@ -1 +1,27 @@ package commands + +import ( + "github.com/hyperledger-labs/orion-sdk-go/examples/util" + "github.com/stretchr/testify/require" + "io/ioutil" + "os" + "testing" +) + +func TestNodeCommand(t *testing.T) { + // 1. Create crypto material and start server + tempDir, err := ioutil.TempDir(os.TempDir(), "ExampleTest") + require.NoError(t, err) + + testServer, _, _, err := util.SetupTestEnv(t, tempDir, uint32(6003)) + require.NoError(t, err) + defer testServer.Stop() + util.StartTestServer(t, testServer) + + // 2. Check cas command response + rootCmd := InitializeOrionCli() + rootCmd.SetArgs([]string{"node"}) + err = rootCmd.Execute() + require.Error(t, err) + require.Equal(t, err.Error(), "not implemented yet") +} diff --git a/cli/commands/root.go b/cli/commands/root.go index 7ddd240..6faf389 100644 --- a/cli/commands/root.go +++ b/cli/commands/root.go @@ -14,7 +14,10 @@ func rootCmd() *cobra.Command { func InitializeOrionCli() *cobra.Command { cmd := rootCmd() - cmd.AddCommand(VersionCmd()) + cmd.AddCommand(versionCmd()) cmd.AddCommand(configCmd()) + cmd.AddCommand(adminCmd()) + cmd.AddCommand(nodeCmd()) + cmd.AddCommand(casCmd()) return cmd } diff --git a/cli/commands/shared_config.go b/cli/commands/shared_config.go new file mode 100644 index 0000000..3594bfe --- /dev/null +++ b/cli/commands/shared_config.go @@ -0,0 +1,115 @@ +package commands + +import ( + orionconfig "github.com/hyperledger-labs/orion-server/config" + "github.com/pkg/errors" + "github.com/spf13/viper" +) + +// SharedConfiguration holds the initial configuration that will be converted into the ledger's genesis block and +// loaded into the database when the server starts with an empty ledger and database. +// +// This struct may also be used to bootstrap a new node into an existing cluster (not yet implemented). +// +// This part of the configuration is replicated and is common to all nodes. +// After the initial bootstrap, this part of the configuration can change only through configuration transactions. +type SharedConfiguration struct { + // Nodes carry the identity, endpoint, and certificate of each database node that serves to clients. + Nodes []*NodeConf + Consensus *ConsensusConf + CAConfig orionconfig.CAConfiguration + Admin []*AdminConf + Ledger LedgerConf +} + +// NodeConf carry the identity, endpoint, and certificate of a database node that serves to clients. +// The NodeID correlates the node definition here with the peer definition in the SharedConfiguration.Consensus. +// The Host and Port are those that are accessible from clients. +// The certificate is the one used to authenticate with clients and validate the server;s signature on +// blocks and transaction/query responses. +type NodeConf struct { + NodeID string + Host string + Port uint32 + CertificatePath string +} + +type ConsensusConf struct { + // The consensus algorithm, currently only "raft" is supported. + Algorithm string + // Peers that take part in consensus. + Members []*PeerConf + // Peers that are allowed to connect and fetch the ledger from members, but do not take part in consensus. + Observers []*PeerConf + // Raft protocol parameters. + RaftConfig *RaftConf +} + +type RaftConf struct { + // Time interval between two Node.Tick invocations. e.g. 100ms. + TickInterval string + // The number of Node.Tick invocations that must pass between elections. + // That is, if a follower does not receive any + // message from the leader of current term before ElectionTick has + // elapsed, it will become candidate and start an election. + // electionTicks must be greater than heartbeatTicks. + ElectionTicks uint32 + // The number of Node.Tick invocations that must + // pass between heartbeats. That is, a leader sends heartbeat + // messages to maintain its leadership every HeartbeatTick ticks. + HeartbeatTicks uint32 + // Limits the max number of in-flight blocks (i.e. Raft messages). + MaxInflightBlocks uint32 + // Take a snapshot when cumulative data since last snapshot exceeds a certain size in bytes. + SnapshotIntervalSize uint64 +} + +// PeerConf defines a server that takes part in consensus, or an observer. +type PeerConf struct { + // The node ID correlates the peer definition here with the NodeConfig.ID field. + NodeId string + // Raft ID must be >0 for members, or =0 for observers. + RaftId uint64 + // The host name or IP address that is used by other peers to connect to this peer. + PeerHost string + // The port that is used by other peers to connect to this peer. + PeerPort uint32 +} + +// AdminConf holds the credentials of the blockchain +// database cluster admin such as the ID and path to +// the x509 certificate +type AdminConf struct { + ID string + CertificatePath string +} + +// LedgerConf defines parameters on the distributed ledger capabilities and algorithms that must be defined uniformly across +// all servers. +type LedgerConf struct { + // StateMerklePatriciaTrieDisabled disables the state Merkle-Patricia-Trie construction. + // With MP-Trie construction disabled, the block's BlockHeader.StateMerkleTreeRootHash field will be nil. + // This flag takes effect on deployment (bootstrap) only, from the first (genesis) block. + // The value of this flag cannot be changed during run-time. + StateMerklePatriciaTrieDisabled bool +} + +// readSharedConfig reads the shared config from the file and returns it. +func readSharedConfig(sharedConfigFile string) (*SharedConfiguration, error) { + if sharedConfigFile == "" { + return nil, errors.New("path to the shared configuration file is empty") + } + + v := viper.New() + v.SetConfigFile(sharedConfigFile) + + if err := v.ReadInConfig(); err != nil { + return nil, errors.Wrapf(err, "error reading shared config file: %s", sharedConfigFile) + } + + sharedConf := &SharedConfiguration{} + if err := v.UnmarshalExact(sharedConf); err != nil { + return nil, errors.Wrapf(err, "unable to unmarshal shared config file: '%s' into struct", sharedConfigFile) + } + return sharedConf, nil +} diff --git a/cli/commands/version.go b/cli/commands/version.go index 6df053f..ad59b6a 100644 --- a/cli/commands/version.go +++ b/cli/commands/version.go @@ -6,7 +6,7 @@ import ( "runtime/debug" ) -func VersionCmd() *cobra.Command { +func versionCmd() *cobra.Command { cmd := &cobra.Command{ Use: "version", Args: cobra.NoArgs, diff --git a/cli/commands/version_test.go b/cli/commands/version_test.go index b73e29c..80cd9c0 100644 --- a/cli/commands/version_test.go +++ b/cli/commands/version_test.go @@ -1,20 +1,30 @@ package commands import ( + "github.com/hyperledger-labs/orion-sdk-go/examples/util" "github.com/stretchr/testify/require" + "io/ioutil" + "os" + "strings" "testing" ) func TestVersionCommand(t *testing.T) { - // 1. Create BCDBHTTPServer and start server - testServer, err := SetupTestServer(t) - defer testServer.Stop() + // 1. Create crypto material and start server + tempDir, err := ioutil.TempDir(os.TempDir(), "ExampleTest") + require.NoError(t, err) + + testServer, _, _, err := util.SetupTestEnv(t, tempDir, uint32(6003)) require.NoError(t, err) - StartTestServer(t, testServer) + defer testServer.Stop() + util.StartTestServer(t, testServer) // 2. Get the version of the CLI by the CLI Version command rootCmd := InitializeOrionCli() rootCmd.SetArgs([]string{"version"}) + b := &strings.Builder{} + rootCmd.SetOut(b) err = rootCmd.Execute() require.NoError(t, err) + require.Contains(t, b.String(), "SDK version: ") } diff --git a/cli/main.go b/cli/main.go index f3e1859..0616107 100644 --- a/cli/main.go +++ b/cli/main.go @@ -4,8 +4,9 @@ package main import ( - "github.com/hyperledger-labs/orion-sdk-go/cli/commands" "os" + + "github.com/hyperledger-labs/orion-sdk-go/cli/commands" ) func main() { diff --git a/cli/testdata/connection-session-config.yml b/cli/testdata/connection-session-config.yml index 2617113..7a927b1 100644 --- a/cli/testdata/connection-session-config.yml +++ b/cli/testdata/connection-session-config.yml @@ -1,7 +1,7 @@ connectionConfig: replicaSet: - - id: "orion-server1" - endpoint: "http://127.0.0.1:6001" + - id: "server1" + endpoint: "http://127.0.0.1:6003" rootCAs: "../testdata/crypto/CA/CA.pem" sessionConfig: diff --git a/cli/testdata/expected-get/CA/CA.key b/cli/testdata/expected-get/CA/CA.key new file mode 100644 index 0000000..8dd95df --- /dev/null +++ b/cli/testdata/expected-get/CA/CA.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIGUsDhRKOiFAh6XFUf6/x5U/ASmqTHK48B3SYYH7vS0YoAoGCCqGSM49 +AwEHoUQDQgAExkt0GIeYarv/1rNGFyd5p72C+WDLFXwCOisInkmSc7Tu0zRQA3Di +XNryteFB7j3jghyObj5E2daP8REMvHphHw== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/expected-get/CA/CA.pem b/cli/testdata/expected-get/CA/CA.pem new file mode 100644 index 0000000..ffd806e --- /dev/null +++ b/cli/testdata/expected-get/CA/CA.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrjCCAVOgAwIBAgIUSw7fbkDu+nx97O39JUYHQfBoFngwCgYIKoZIzj0EAwIw +LDELMAkGA1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4X +DTIzMDgxNTExMTAzM1oXDTI4MDgxMzExMTAzM1owLDELMAkGA1UEBhMCSUwxDjAM +BgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAExkt0GIeYarv/1rNGFyd5p72C+WDLFXwCOisInkmSc7Tu0zRQA3DiXNry +teFB7j3jghyObj5E2daP8REMvHphH6NTMFEwHQYDVR0OBBYEFLOBDYmHGI9R3Zjb +AhVCaCyCAyh0MB8GA1UdIwQYMBaAFLOBDYmHGI9R3ZjbAhVCaCyCAyh0MA8GA1Ud +EwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAILCEE8eBycJxkTsu5j0YUuZ +upInvfOD+wo8xePgfcOWAiEAmd3kY7lE9PZW5n11TrkzK3ZzNdqltxQdHk26QWi0 +4Sw= +-----END CERTIFICATE----- diff --git a/cli/testdata/expected-get/admin/admin.key b/cli/testdata/expected-get/admin/admin.key new file mode 100644 index 0000000..270ac8a --- /dev/null +++ b/cli/testdata/expected-get/admin/admin.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAvsUgUBcRggQL11GNeSHHc1uxtw1SS06KJ++8p99xo7oAoGCCqGSM49 +AwEHoUQDQgAE61ICL+iyVjOWbtB+HvKOhrvr7ZVRlzUoijCQ/1YCILNjNuEz/2zO +n71YT7FbmIrw2hkkOWJOWVcwUN9Pq+SxiQ== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/expected-get/admin/admin.pem b/cli/testdata/expected-get/admin/admin.pem new file mode 100644 index 0000000..6e7130d --- /dev/null +++ b/cli/testdata/expected-get/admin/admin.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUTCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbkwCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTAzNloXDTI4MDgxMzExMTAzNlowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +61ICL+iyVjOWbtB+HvKOhrvr7ZVRlzUoijCQ/1YCILNjNuEz/2zOn71YT7FbmIrw +2hkkOWJOWVcwUN9Pq+SxiTAKBggqhkjOPQQDAgNHADBEAiAr+moYKnQRWaMY+oc9 +PRyNo8NvH0KtDGtOHAbeeixAYQIgGAgO+DikDu48tJsrpepiOaQlpecaJxegvPDq +jFbxZgI= +-----END CERTIFICATE----- diff --git a/cli/testdata/expected-get/alice/alice.key b/cli/testdata/expected-get/alice/alice.key new file mode 100644 index 0000000..0b86dfd --- /dev/null +++ b/cli/testdata/expected-get/alice/alice.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEILqn8X+9beiLlaWwJUYpel9RRrVqdrV08TkhwvVPcTjEoAoGCCqGSM49 +AwEHoUQDQgAERPIvzg+/GJxAr4uRwrcZHOMVwMBZCRLBDbFIqAb1ssOHpy4pc4Ou +loy19xbUMYIqmHogj4fxYP+4yYHkCZpB6Q== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/expected-get/alice/alice.pem b/cli/testdata/expected-get/alice/alice.pem new file mode 100644 index 0000000..be20a06 --- /dev/null +++ b/cli/testdata/expected-get/alice/alice.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUTCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbswCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTAzOVoXDTI4MDgxMzExMTAzOVowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +RPIvzg+/GJxAr4uRwrcZHOMVwMBZCRLBDbFIqAb1ssOHpy4pc4Ouloy19xbUMYIq +mHogj4fxYP+4yYHkCZpB6TAKBggqhkjOPQQDAgNHADBEAiB5L1uiSClq23hg7doM +39yS4bF0jrm4RFtwhZThMzk6JwIgXLvsMt2GUqI5BbeIoOhAD8pe36/xo+rQvr5L +NHNxGTA= +-----END CERTIFICATE----- diff --git a/cli/testdata/expected-get/bob/bob.key b/cli/testdata/expected-get/bob/bob.key new file mode 100644 index 0000000..6738303 --- /dev/null +++ b/cli/testdata/expected-get/bob/bob.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIM1bno3o4CGWOF1bCG1yoUzVpXshqm0OhQjLPvpK+Xi7oAoGCCqGSM49 +AwEHoUQDQgAEF5n4V9ssnDF2X9zyTlBYYc8Gk/nLsDLwoWkivE7yvjLFntzwjxd7 +d2eghjg6A5jziZc0pwKPV8uxWB2Lo58pHQ== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/expected-get/bob/bob.pem b/cli/testdata/expected-get/bob/bob.pem new file mode 100644 index 0000000..fa67e58 --- /dev/null +++ b/cli/testdata/expected-get/bob/bob.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUjCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbwwCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTA0MVoXDTI4MDgxMzExMTA0MVowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +F5n4V9ssnDF2X9zyTlBYYc8Gk/nLsDLwoWkivE7yvjLFntzwjxd7d2eghjg6A5jz +iZc0pwKPV8uxWB2Lo58pHTAKBggqhkjOPQQDAgNIADBFAiBIXstNmxSomEW4JbqP +gmLq7xx+8lsOXuPfIt6zfrsObQIhALCFnuuvKgPNQdABIOAWkkUb+4SP9/Ug+5wf +M+RjZEe6 +-----END CERTIFICATE----- diff --git a/cli/testdata/expected-get/server/server.key b/cli/testdata/expected-get/server/server.key new file mode 100644 index 0000000..bb744e3 --- /dev/null +++ b/cli/testdata/expected-get/server/server.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIBP61HSKvKxnUna0avdlC6bXTiDSSRnqLtpB0jDOoLppoAoGCCqGSM49 +AwEHoUQDQgAEGRFEgxhNRRKEZCEOZEafGjUAxaRptFZ2ykyp4CMYzxBkGNFyTTV6 +/bokrlOmt+nINP9yLyzVdkdAZwUgiqjPcg== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/expected-get/server/server.pem b/cli/testdata/expected-get/server/server.pem new file mode 100644 index 0000000..d3f40eb --- /dev/null +++ b/cli/testdata/expected-get/server/server.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUjCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbgwCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTAzNVoXDTI4MDgxMzExMTAzNVowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +GRFEgxhNRRKEZCEOZEafGjUAxaRptFZ2ykyp4CMYzxBkGNFyTTV6/bokrlOmt+nI +NP9yLyzVdkdAZwUgiqjPcjAKBggqhkjOPQQDAgNIADBFAiEAxbS+eeATmcl76zR9 +SZ1/N0NxQstC7naI4VQxQSUWCysCIFx5skwiTZzFtlVRuvhytU/x/iw880MLwa6x +TJgahAQh +-----END CERTIFICATE----- diff --git a/cli/testdata/expected-get/user/user.key b/cli/testdata/expected-get/user/user.key new file mode 100644 index 0000000..037fb8a --- /dev/null +++ b/cli/testdata/expected-get/user/user.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIGp8OxlyV/yl3hcwYhs++KXCtT0M3A70ck66eurCK2QooAoGCCqGSM49 +AwEHoUQDQgAEHyVPgc5EyZna9Sf777lNrRJ6L9+J254hl8YpH6ya+S04DXXFOkSL +iEHCJPWY0KB/1KmWv75YzmHliBSRqflLUg== +-----END EC PRIVATE KEY----- diff --git a/cli/testdata/expected-get/user/user.pem b/cli/testdata/expected-get/user/user.pem new file mode 100644 index 0000000..6042490 --- /dev/null +++ b/cli/testdata/expected-get/user/user.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBUjCB+QIUKKzjaaD7M42PbORuyLdj4hiYPbowCgYIKoZIzj0EAwIwLDELMAkG +A1UEBhMCSUwxDjAMBgNVBAgMBUhhaWZhMQ0wCwYDVQQKDARCQ0RCMB4XDTIzMDgx +NTExMTAzOFoXDTI4MDgxMzExMTAzOFowLDELMAkGA1UEBhMCSUwxDjAMBgNVBAgM +BUhhaWZhMQ0wCwYDVQQKDARCQ0RCMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +HyVPgc5EyZna9Sf777lNrRJ6L9+J254hl8YpH6ya+S04DXXFOkSLiEHCJPWY0KB/ +1KmWv75YzmHliBSRqflLUjAKBggqhkjOPQQDAgNIADBFAiAhS96L6Ieea5hq4XbJ +5/WUFNtbqzFpKgbIyeWRB+uHxQIhAOlJO5Gqr+F8BKZwckBdjEmHo25mqH1UnOZX +nwjXtnrk +-----END CERTIFICATE-----