diff --git a/cli/cluster/cluster.go b/cli/cluster/cluster.go index f62c63f27..5b6fd12d6 100644 --- a/cli/cluster/cluster.go +++ b/cli/cluster/cluster.go @@ -296,7 +296,8 @@ func Instantiate(cluster ClusterConfig, name string) *Config { // collectEtcdConfig collects a configuration from etcd with options from // the cluster configuration. -func collectEtcdConfig(clusterConfig ClusterConfig) (*Config, error) { +func collectEtcdConfig(collectors CollectorFactory, + clusterConfig ClusterConfig) (*Config, error) { etcdConfig := clusterConfig.Config.Etcd opts := EtcdOpts{ Endpoints: etcdConfig.Endpoints, @@ -328,7 +329,11 @@ func collectEtcdConfig(clusterConfig ClusterConfig) (*Config, error) { } defer etcd.Close() - etcdCollector := NewEtcdAllCollector(etcd, etcdConfig.Prefix, opts.Timeout) + etcdCollector, err := collectors.NewEtcd(etcd, etcdConfig.Prefix, "", opts.Timeout) + if err != nil { + return nil, fmt.Errorf("failed to create etcd collector: %w", err) + } + etcdRawConfig, err := etcdCollector.Collect() if err != nil { return nil, fmt.Errorf("unable to get config from etcd: %w", err) @@ -338,7 +343,8 @@ func collectEtcdConfig(clusterConfig ClusterConfig) (*Config, error) { // collectTarantoolConfig collects a configuration from tarantool config // storage with options from the tarantool configuration. -func collectTarantoolConfig(clusterConfig ClusterConfig) (*Config, error) { +func collectTarantoolConfig(collectors CollectorFactory, + clusterConfig ClusterConfig) (*Config, error) { tarantoolConfig := clusterConfig.Config.Storage var opts []connector.ConnectOpts for _, endpoint := range tarantoolConfig.Endpoints { @@ -379,9 +385,13 @@ func collectTarantoolConfig(clusterConfig ClusterConfig) (*Config, error) { } defer pool.Close() - tarantoolCollector := NewTarantoolAllCollector(pool, - tarantoolConfig.Prefix, + tarantoolCollector, err := collectors.NewTarantool(pool, + tarantoolConfig.Prefix, "", time.Duration(tarantoolConfig.Timeout*float64(time.Second))) + if err != nil { + return nil, fmt.Errorf("failed to create tarantool config storage collector: %w", err) + } + tarantoolRawConfig, err := tarantoolCollector.Collect() if err != nil { return nil, fmt.Errorf("failed to get config from tarantool config storage: %w", err) @@ -393,7 +403,7 @@ func collectTarantoolConfig(clusterConfig ClusterConfig) (*Config, error) { // a config file. It uses a a config file, etcd and default environment // variables as sources. The function returns a cluster config as is, without // merging of settings from scopes: global, group, replicaset, instance. -func GetClusterConfig(path string) (ClusterConfig, error) { +func GetClusterConfig(collectors CollectorFactory, path string) (ClusterConfig, error) { ret := ClusterConfig{} if path == "" { return ret, fmt.Errorf("a configuration file must be set") @@ -408,7 +418,11 @@ func GetClusterConfig(path string) (ClusterConfig, error) { } config.Merge(mainEnvConfig) - collector := NewFileCollector(path) + collector, err := collectors.NewFile(path) + if err != nil { + return ret, fmt.Errorf("failed to create a file collector: %w", err) + } + fileConfig, err := collector.Collect() if err != nil { fmtErr := "unable to get cluster config from %q: %w" @@ -421,7 +435,7 @@ func GetClusterConfig(path string) (ClusterConfig, error) { return ret, fmt.Errorf("unable to parse cluster config from file: %w", err) } if len(clusterConfig.Config.Etcd.Endpoints) > 0 { - etcdConfig, err := collectEtcdConfig(clusterConfig) + etcdConfig, err := collectEtcdConfig(collectors, clusterConfig) if err != nil { return ret, err } @@ -429,7 +443,7 @@ func GetClusterConfig(path string) (ClusterConfig, error) { } if len(clusterConfig.Config.Storage.Endpoints) > 0 { - tarantoolConfig, err := collectTarantoolConfig(clusterConfig) + tarantoolConfig, err := collectTarantoolConfig(collectors, clusterConfig) if err != nil { return ret, err } diff --git a/cli/cluster/cluster_test.go b/cli/cluster/cluster_test.go index 78b65a97b..f2840c198 100644 --- a/cli/cluster/cluster_test.go +++ b/cli/cluster/cluster_test.go @@ -350,7 +350,8 @@ func TestInstantiate_inherbit(t *testing.T) { } func TestGetClusterConfig_path(t *testing.T) { - config, err := cluster.GetClusterConfig("testdata/app/config.yaml") + collectors := cluster.NewCollectorFactory() + config, err := cluster.GetClusterConfig(collectors, "testdata/app/config.yaml") require.NoError(t, err) assert.Equal(t, `app: @@ -426,7 +427,8 @@ too: 3 func TestGetClusterConfig_environment(t *testing.T) { os.Setenv("TT_WAL_DIR_DEFAULT", "envdir") os.Setenv("TT_WAL_MODE_DEFAULT", "envmode") - config, err := cluster.GetClusterConfig("testdata/app/config.yaml") + collectors := cluster.NewCollectorFactory() + config, err := cluster.GetClusterConfig(collectors, "testdata/app/config.yaml") os.Unsetenv("TT_WAL_DIR_DEFAULT") os.Unsetenv("TT_WAL_MODE_DEFAULT") @@ -464,14 +466,16 @@ wal: } func TestGetClusterConfig_invalid_apppath(t *testing.T) { - config, err := cluster.GetClusterConfig("some/non/exist") + collectors := cluster.NewCollectorFactory() + config, err := cluster.GetClusterConfig(collectors, "some/non/exist") assert.Error(t, err) assert.NotNil(t, config) } func TestGetClusterConfig_nopath(t *testing.T) { - config, err := cluster.GetClusterConfig("") + collectors := cluster.NewCollectorFactory() + config, err := cluster.GetClusterConfig(collectors, "") expected := "a configuration file must be set" assert.EqualError(t, err, expected) @@ -479,7 +483,8 @@ func TestGetClusterConfig_nopath(t *testing.T) { } func TestGetInstanceConfig_file(t *testing.T) { - cconfig, err := cluster.GetClusterConfig("testdata/app/config.yaml") + collectors := cluster.NewCollectorFactory() + cconfig, err := cluster.GetClusterConfig(collectors, "testdata/app/config.yaml") require.NoError(t, err) config, err := cluster.GetInstanceConfig(cconfig, "c") @@ -498,7 +503,8 @@ zoo: 2 } func TestGetInstanceConfig_environment(t *testing.T) { - cconfig, err := cluster.GetClusterConfig("testdata/app/config.yaml") + collectors := cluster.NewCollectorFactory() + cconfig, err := cluster.GetClusterConfig(collectors, "testdata/app/config.yaml") require.NoError(t, err) os.Setenv("TT_WAL_DIR", "envdir") config, err := cluster.GetInstanceConfig(cconfig, "c") @@ -519,7 +525,8 @@ zoo: 2 } func TestGetInstanceConfig_noinstance(t *testing.T) { - cconfig, err := cluster.GetClusterConfig("testdata/app/config.yaml") + collectors := cluster.NewCollectorFactory() + cconfig, err := cluster.GetClusterConfig(collectors, "testdata/app/config.yaml") require.NoError(t, err) _, err = cluster.GetInstanceConfig(cconfig, "unknown") expected := "an instance \"unknown\" not found" diff --git a/cli/cluster/cmd/common.go b/cli/cluster/cmd/common.go index 98ef355dd..dc436d254 100644 --- a/cli/cluster/cmd/common.go +++ b/cli/cluster/cmd/common.go @@ -184,7 +184,10 @@ func connectEtcd(uriOpts UriOpts, connOpts connectOpts) (*clientv3.Client, error } // createPublisher creates a new data publisher and collector based on UriOpts. -func createPublisherAndCollector(connOpts connectOpts, +func createPublisherAndCollector( + publishers cluster.DataPublisherFactory, + collectors cluster.CollectorFactory, + connOpts connectOpts, opts UriOpts) (cluster.DataPublisher, cluster.Collector, func(), error) { prefix, key, timeout := opts.Prefix, opts.Key, opts.Timeout @@ -193,13 +196,22 @@ func createPublisherAndCollector(connOpts connectOpts, var ( publisher cluster.DataPublisher collector cluster.Collector + err error ) - if key == "" { - publisher = cluster.NewTarantoolAllDataPublisher(conn, prefix, timeout) - collector = cluster.NewTarantoolAllCollector(conn, prefix, timeout) - } else { - publisher = cluster.NewTarantoolKeyDataPublisher(conn, prefix, key, timeout) - collector = cluster.NewTarantoolKeyCollector(conn, prefix, key, timeout) + if publishers != nil { + publisher, err = publishers.NewTarantool(conn, prefix, key, timeout) + if err != nil { + return nil, nil, nil, + fmt.Errorf("failed to create tarantool config storage publisher: %w", err) + } + } + + if collectors != nil { + collector, err = collectors.NewTarantool(conn, prefix, key, timeout) + if err != nil { + return nil, nil, nil, + fmt.Errorf("failed to create tarantool config storage collector: %w", err) + } } return publisher, collector, func() { conn.Close() }, nil } @@ -209,13 +221,22 @@ func createPublisherAndCollector(connOpts connectOpts, var ( publisher cluster.DataPublisher collector cluster.Collector + err error ) - if key == "" { - publisher = cluster.NewEtcdAllDataPublisher(etcdcli, prefix, timeout) - collector = cluster.NewEtcdAllCollector(etcdcli, prefix, timeout) - } else { - publisher = cluster.NewEtcdKeyDataPublisher(etcdcli, prefix, key, timeout) - collector = cluster.NewEtcdKeyCollector(etcdcli, prefix, key, timeout) + if publishers != nil { + publisher, err = publishers.NewEtcd(etcdcli, prefix, key, timeout) + if err != nil { + return nil, nil, nil, + fmt.Errorf("failed to create etcd publisher: %w", err) + } + } + + if collectors != nil { + collector, err = collectors.NewEtcd(etcdcli, prefix, key, timeout) + if err != nil { + return nil, nil, nil, + fmt.Errorf("failed to create etcd collector: %w", err) + } } return publisher, collector, func() { etcdcli.Close() }, nil } diff --git a/cli/cluster/cmd/publish.go b/cli/cluster/cmd/publish.go index 20257bdb0..5daba4ee4 100644 --- a/cli/cluster/cmd/publish.go +++ b/cli/cluster/cmd/publish.go @@ -17,6 +17,10 @@ type PublishCtx struct { // Force defines whether the publish should be forced and a validation step // is omitted. Force bool + // Publishers defines a used data publishers factory. + Publishers cluster.DataPublisherFactory + // Collectors defines a used collectors factory. + Collectors cluster.CollectorFactory // Src is a raw data to publish. Src []byte // Config is a parsed raw data configuration to publish. @@ -39,7 +43,10 @@ func PublishUri(publishCtx PublishCtx, uri *url.URL) error { Username: publishCtx.Username, Password: publishCtx.Password, } - publisher, collector, cancel, err := createPublisherAndCollector(connOpts, uriOpts) + publisher, collector, cancel, err := createPublisherAndCollector( + publishCtx.Publishers, + publishCtx.Collectors, + connOpts, uriOpts) if err != nil { return err } @@ -59,13 +66,21 @@ func PublishCluster(publishCtx PublishCtx, path, instance string) error { return err } - publisher := cluster.NewFileDataPublisher(path) + publisher, err := publishCtx.Publishers.NewFile(path) + if err != nil { + return fmt.Errorf("failed to create a file publisher: %w", err) + } + if instance == "" { // The easy case, just publish the configuration as is. return publisher.Publish(publishCtx.Src) } - collector := cluster.NewFileCollector(path) + collector, err := publishCtx.Collectors.NewFile(path) + if err != nil { + return fmt.Errorf("failed to create a file collector: %w", err) + } + return replaceInstanceConfig(instance, publishCtx.Config, collector, publisher) } diff --git a/cli/cluster/cmd/show.go b/cli/cluster/cmd/show.go index 472c0370c..0938770cb 100644 --- a/cli/cluster/cmd/show.go +++ b/cli/cluster/cmd/show.go @@ -13,6 +13,8 @@ type ShowCtx struct { Username string // Password defines a password for connection. Password string + // Collectors defines a used collectors factory. + Collectors cluster.CollectorFactory // Validate defines whether the command will check the showed // configuration. Validate bool @@ -29,7 +31,10 @@ func ShowUri(showCtx ShowCtx, uri *url.URL) error { Username: showCtx.Username, Password: showCtx.Password, } - _, collector, cancel, err := createPublisherAndCollector(connOpts, uriOpts) + _, collector, cancel, err := createPublisherAndCollector( + nil, + showCtx.Collectors, + connOpts, uriOpts) if err != nil { return err } @@ -50,7 +55,7 @@ func ShowUri(showCtx ShowCtx, uri *url.URL) error { // ShowCluster shows a full cluster configuration for a configuration path. func ShowCluster(showCtx ShowCtx, path, name string) error { - config, err := cluster.GetClusterConfig(path) + config, err := cluster.GetClusterConfig(showCtx.Collectors, path) if err != nil { return fmt.Errorf("failed to get a cluster configuration: %w", err) } diff --git a/cli/cluster/collectorfactory.go b/cli/cluster/collectorfactory.go new file mode 100644 index 000000000..c57a13b65 --- /dev/null +++ b/cli/cluster/collectorfactory.go @@ -0,0 +1,54 @@ +package cluster + +import ( + "time" + + clientv3 "go.etcd.io/etcd/client/v3" + + "github.com/tarantool/tt/cli/connector" +) + +// CollectorFactory creates new data collectors. +type CollectorFactory interface { + // NewFile creates a new data collector to collect configuration from a file. + NewFile(path string) (Collector, error) + // NewEtcd creates a new data collector to collect configuration from etcd. + NewEtcd(etcdcli *clientv3.Client, + prefix, key string, timeout time.Duration) (Collector, error) + // NewTarantool creates a new data collector to collect configuration from + // tarantool config storage. + NewTarantool(conn connector.Connector, + prefix, key string, timeout time.Duration) (Collector, error) +} + +// collectorsFactory is a type that implements a default CollectorFactory. +type collectorsFactory struct{} + +// NewCollectorFactory creates a new CollectorFactory. +func NewCollectorFactory() CollectorFactory { + return collectorsFactory{} +} + +// NewFiler creates a new file configuration collector. +func (factory collectorsFactory) NewFile(path string) (Collector, error) { + return NewFileCollector(path), nil +} + +// NewEtcd creates a new etcd configuration collector. +func (factory collectorsFactory) NewEtcd(etcdcli *clientv3.Client, + prefix, key string, timeout time.Duration) (Collector, error) { + if key == "" { + return NewEtcdAllCollector(etcdcli, prefix, timeout), nil + } + return NewEtcdKeyCollector(etcdcli, prefix, key, timeout), nil +} + +// NewTarantool creates creates a new tarantool config storage configuration +// collector. +func (factory collectorsFactory) NewTarantool(conn connector.Connector, + prefix, key string, timeout time.Duration) (Collector, error) { + if key == "" { + return NewTarantoolAllCollector(conn, prefix, timeout), nil + } + return NewTarantoolKeyCollector(conn, prefix, key, timeout), nil +} diff --git a/cli/cluster/collectorfactory_test.go b/cli/cluster/collectorfactory_test.go new file mode 100644 index 000000000..dc3fed719 --- /dev/null +++ b/cli/cluster/collectorfactory_test.go @@ -0,0 +1,61 @@ +package cluster_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + clientv3 "go.etcd.io/etcd/client/v3" + + "github.com/tarantool/tt/cli/cluster" + "github.com/tarantool/tt/cli/connector" +) + +func TestCollectorFactory(t *testing.T) { + etcdcli := &clientv3.Client{} + conn := &connector.BinaryConnector{} + factory := cluster.NewCollectorFactory() + + noErr := func(publisher cluster.Collector, err error) cluster.Collector { + require.NoError(t, err) + return publisher + } + + cases := []struct { + Name string + Collector cluster.Collector + Expected cluster.Collector + }{ + { + Name: "file", + Collector: noErr(factory.NewFile("foo")), + Expected: cluster.NewFileCollector("foo"), + }, + { + Name: "etcd_all", + Collector: noErr(factory.NewEtcd(etcdcli, "foo", "", 1)), + Expected: cluster.NewEtcdAllCollector(etcdcli, "foo", 1), + }, + { + Name: "etcd_key", + Collector: noErr(factory.NewEtcd(etcdcli, "foo", "bar", 2)), + Expected: cluster.NewEtcdKeyCollector(etcdcli, "foo", "bar", 2), + }, + { + Name: "tarantool_all", + Collector: noErr(factory.NewTarantool(conn, "foo", "", 1)), + Expected: cluster.NewTarantoolAllCollector(conn, "foo", 1), + }, + { + Name: "tarantool_key", + Collector: noErr(factory.NewTarantool(conn, "foo", "bar", 2)), + Expected: cluster.NewTarantoolKeyCollector(conn, "foo", "bar", 2), + }, + } + + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + assert.Equal(t, tc.Expected, tc.Collector) + }) + } +} diff --git a/cli/cluster/integration_test.go b/cli/cluster/integration_test.go index 8a49858a9..88fdb12da 100644 --- a/cli/cluster/integration_test.go +++ b/cli/cluster/integration_test.go @@ -422,7 +422,8 @@ func TestGetClusterConfig_etcd(t *testing.T) { `) os.Setenv("TT_WAL_MODE_DEFAULT", "envmode") os.Setenv("TT_WAL_MAX_SIZE_DEFAULT", "envsize") - config, err := cluster.GetClusterConfig("testdata/etcdapp/config.yaml") + collectors := cluster.NewCollectorFactory() + config, err := cluster.GetClusterConfig(collectors, "testdata/etcdapp/config.yaml") os.Unsetenv("TT_WAL_MODE_DEFAULT") os.Unsetenv("TT_WAL_MAX_SIZE_DEFAULT") @@ -500,7 +501,8 @@ func TestGetClusterConfig_etcd_connect_from_env(t *testing.T) { os.Setenv("TT_CONFIG_ETCD_PASSWORD", pass) os.Setenv("TT_CONFIG_ETCD_PREFIX", prefix) - config, err := cluster.GetClusterConfig("testdata/etcdapp/config.yaml") + collectors := cluster.NewCollectorFactory() + config, err := cluster.GetClusterConfig(collectors, "testdata/etcdapp/config.yaml") os.Unsetenv("TT_CONFIG_ETCD_USERNAME") os.Unsetenv("TT_CONFIG_ETCD_PASSWORD") diff --git a/cli/cluster/publisherfactory.go b/cli/cluster/publisherfactory.go new file mode 100644 index 000000000..fe7e3e1a7 --- /dev/null +++ b/cli/cluster/publisherfactory.go @@ -0,0 +1,53 @@ +package cluster + +import ( + "time" + + clientv3 "go.etcd.io/etcd/client/v3" + + "github.com/tarantool/tt/cli/connector" +) + +// DataPublisherFactory creates new data publishers. +type DataPublisherFactory interface { + // NewFile creates a new data publisher to publish data into a file. + NewFile(path string) (DataPublisher, error) + // NewEtcd creates a new data publisher to publish data into etcd. + NewEtcd(etcdcli *clientv3.Client, + prefix, key string, timeout time.Duration) (DataPublisher, error) + // NewTarantool creates a new data publisher to publish data into tarantool + // config storage. + NewTarantool(conn connector.Connector, + prefix, key string, timeout time.Duration) (DataPublisher, error) +} + +// publishersFactory is a type that implements a default DataPublisherFactory. +type publishersFactory struct{} + +// NewDataPublisherFactory creates a new DataPublisherFactory. +func NewDataPublisherFactory() DataPublisherFactory { + return publishersFactory{} +} + +// NewFiler creates a new file data publisher. +func (factory publishersFactory) NewFile(path string) (DataPublisher, error) { + return NewFileDataPublisher(path), nil +} + +// NewEtcd creates a new etcd data publisher. +func (factory publishersFactory) NewEtcd(etcdcli *clientv3.Client, + prefix, key string, timeout time.Duration) (DataPublisher, error) { + if key == "" { + return NewEtcdAllDataPublisher(etcdcli, prefix, timeout), nil + } + return NewEtcdKeyDataPublisher(etcdcli, prefix, key, timeout), nil +} + +// NewTarantool creates creates a new tarantool config storage data publisher. +func (factory publishersFactory) NewTarantool(conn connector.Connector, + prefix, key string, timeout time.Duration) (DataPublisher, error) { + if key == "" { + return NewTarantoolAllDataPublisher(conn, prefix, timeout), nil + } + return NewTarantoolKeyDataPublisher(conn, prefix, key, timeout), nil +} diff --git a/cli/cluster/publisherfactory_test.go b/cli/cluster/publisherfactory_test.go new file mode 100644 index 000000000..6916f90f2 --- /dev/null +++ b/cli/cluster/publisherfactory_test.go @@ -0,0 +1,61 @@ +package cluster_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + clientv3 "go.etcd.io/etcd/client/v3" + + "github.com/tarantool/tt/cli/cluster" + "github.com/tarantool/tt/cli/connector" +) + +func TestDataPublisherFactory(t *testing.T) { + etcdcli := &clientv3.Client{} + conn := &connector.BinaryConnector{} + factory := cluster.NewDataPublisherFactory() + + noErr := func(publisher cluster.DataPublisher, err error) cluster.DataPublisher { + require.NoError(t, err) + return publisher + } + + cases := []struct { + Name string + Publisher cluster.DataPublisher + Expected cluster.DataPublisher + }{ + { + Name: "file", + Publisher: noErr(factory.NewFile("foo")), + Expected: cluster.NewFileDataPublisher("foo"), + }, + { + Name: "etcd_all", + Publisher: noErr(factory.NewEtcd(etcdcli, "foo", "", 1)), + Expected: cluster.NewEtcdAllDataPublisher(etcdcli, "foo", 1), + }, + { + Name: "etcd_key", + Publisher: noErr(factory.NewEtcd(etcdcli, "foo", "bar", 2)), + Expected: cluster.NewEtcdKeyDataPublisher(etcdcli, "foo", "bar", 2), + }, + { + Name: "tarantool_all", + Publisher: noErr(factory.NewTarantool(conn, "foo", "", 1)), + Expected: cluster.NewTarantoolAllDataPublisher(conn, "foo", 1), + }, + { + Name: "tarantool_key", + Publisher: noErr(factory.NewTarantool(conn, "foo", "bar", 2)), + Expected: cluster.NewTarantoolKeyDataPublisher(conn, "foo", "bar", 2), + }, + } + + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + assert.Equal(t, tc.Expected, tc.Publisher) + }) + } +} diff --git a/cli/cmd/cluster.go b/cli/cmd/cluster.go index 90b39274a..9c221a803 100644 --- a/cli/cmd/cluster.go +++ b/cli/cmd/cluster.go @@ -15,6 +15,7 @@ import ( "github.com/tarantool/tt/cli/cmd/internal" "github.com/tarantool/tt/cli/cmdcontext" "github.com/tarantool/tt/cli/connect" + "github.com/tarantool/tt/cli/integrity" "github.com/tarantool/tt/cli/modules" "github.com/tarantool/tt/cli/running" ) @@ -35,6 +36,10 @@ var publishCtx = clustercmd.PublishCtx{ Force: false, } +var ( + publishIntegrityPrivateKey string +) + func NewClusterCmd() *cobra.Command { clusterCmd := &cobra.Command{ Use: "cluster", @@ -145,6 +150,9 @@ environment variables < command flags < URL credentials. "password (used as etcd credentials only)") publish.Flags().BoolVar(&publishCtx.Force, "force", publishCtx.Force, "force publish and skip validation") + // Integrity flags. + integrity.RegisterWithIntegrityFlag(publish.Flags(), &publishIntegrityPrivateKey) + clusterCmd.AddCommand(publish) return clusterCmd @@ -152,6 +160,16 @@ environment variables < command flags < URL credentials. // internalClusterShowModule is an entrypoint for `cluster show` command. func internalClusterShowModule(cmdCtx *cmdcontext.CmdCtx, args []string) error { + // TODO: create integrity collectors factory from the command context if + // needed instead of the global one. + collectors, err := integrity.NewCollectorFactory() + if err == integrity.ErrNotConfigured { + collectors = cluster.NewCollectorFactory() + } else if err != nil { + return fmt.Errorf("failed to create collectors with integrity check: %w", err) + } + showCtx.Collectors = collectors + if uri, ok := parseUrl(args[0]); ok { return clustercmd.ShowUri(showCtx, uri) } @@ -171,6 +189,27 @@ func internalClusterShowModule(cmdCtx *cmdcontext.CmdCtx, args []string) error { // internalClusterPublishModule is an entrypoint for `cluster publish` command. func internalClusterPublishModule(cmdCtx *cmdcontext.CmdCtx, args []string) error { + // TODO: create integrity collectors factory from the command context if + // needed instead of the global one. + collectors, err := integrity.NewCollectorFactory() + if err == integrity.ErrNotConfigured { + collectors = cluster.NewCollectorFactory() + } else if err != nil { + return fmt.Errorf("failed to create collectors with integrity check: %w", err) + } + publishCtx.Collectors = collectors + + if publishIntegrityPrivateKey != "" { + key := publishIntegrityPrivateKey + publishers, err := integrity.NewDataPublisherFactory(key) + if err != nil { + return fmt.Errorf("failed to create publishers with integrity: %w", err) + } + publishCtx.Publishers = publishers + } else { + publishCtx.Publishers = cluster.NewDataPublisherFactory() + } + data, config, err := readSourceFile(args[1]) if err != nil { return err diff --git a/cli/integrity/go.mod b/cli/integrity/go.mod deleted file mode 100644 index 44ddb2d36..000000000 --- a/cli/integrity/go.mod +++ /dev/null @@ -1,17 +0,0 @@ -module github.com/tarantool/tt/cli/integrity - -go 1.20 - -require ( - github.com/spf13/cobra v1.8.0 - github.com/stretchr/testify v1.8.4 -) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/cli/integrity/go.sum b/cli/integrity/go.sum deleted file mode 100644 index a373726f6..000000000 --- a/cli/integrity/go.sum +++ /dev/null @@ -1,24 +0,0 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cli/integrity/integrity.go b/cli/integrity/integrity.go index 1bb85f7a0..ba5887e70 100644 --- a/cli/integrity/integrity.go +++ b/cli/integrity/integrity.go @@ -4,6 +4,12 @@ import ( "errors" "github.com/spf13/pflag" + + "github.com/tarantool/tt/cli/cluster" +) + +var ( + ErrNotConfigured = errors.New("integration check is not configured") ) var FileRepository Repository = dummyRepository{} @@ -37,3 +43,15 @@ func RegisterIntegrityCheckPeriodFlag(flagset *pflag.FlagSet, dst *int) {} func InitializeIntegrityCheck(publicKeyPath string, configDir string) error { return errors.New("integrity checks should never be initialized in ce") } + +// NewCollectorFactory creates a new CollectorFactory with integrity checks +// in collectors. In the CE implementation it always returns ErrNotConfigured. +func NewCollectorFactory() (cluster.CollectorFactory, error) { + return nil, ErrNotConfigured +} + +// NewDataPublisherFactory create a new DataPublisherFactory with integrity +// algorithms in publishers. Should be never be called in the CE. +func NewDataPublisherFactory(path string) (cluster.DataPublisherFactory, error) { + return nil, errors.New("integrity publishers should never be created in ce") +} diff --git a/cli/integrity/integrity_test.go b/cli/integrity/integrity_test.go index 51df53eac..4bb54ec19 100644 --- a/cli/integrity/integrity_test.go +++ b/cli/integrity/integrity_test.go @@ -27,7 +27,8 @@ func TestNewSigner(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { signer, err := integrity.NewSigner(testCase.privateKeyPath) require.Nil(t, signer, "signer must not be created") - require.EqualError(t, err, "integrity signer should never be created in ce", "an error should be produced") + require.EqualError(t, err, "integrity signer should never be created in ce", + "an error should be produced") }) } } @@ -63,7 +64,9 @@ func InitializeIntegrityCheck(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { err := integrity.InitializeIntegrityCheck(testCase.publicKeyPath, testCase.configDir) - require.EqualError(t, err, "integrity checks should never be initialized in ce", "an error should be produced") + require.EqualError(t, err, + "integrity checks should never be initialized in ce", + "an error should be produced") }) } } @@ -193,3 +196,17 @@ func TestRegisterIntegrityCheckPeriodFlag(t *testing.T) { }) } } + +func TestNewCollectorFactory(t *testing.T) { + factory, err := integrity.NewCollectorFactory() + + require.Nil(t, factory) + require.Equal(t, err, integrity.ErrNotConfigured) +} + +func TestNewPublisherFactory(t *testing.T) { + factory, err := integrity.NewDataPublisherFactory("") + + require.Nil(t, factory) + require.EqualError(t, err, "integrity publishers should never be created in ce") +} diff --git a/cli/running/running.go b/cli/running/running.go index 3bb735706..5d94896c3 100644 --- a/cli/running/running.go +++ b/cli/running/running.go @@ -311,7 +311,18 @@ func loadInstanceConfig(configPath, instName string) (cluster.InstanceConfig, er if configPath == "" { return instCfg, nil } - clusterCfg, err := cluster.GetClusterConfig(configPath) + + // TODO: create integrity collectors factory from the command context if + // needed instead of the global one. + collectors, err := integrity.NewCollectorFactory() + if err == integrity.ErrNotConfigured { + collectors = cluster.NewCollectorFactory() + } else if err != nil { + return instCfg, + fmt.Errorf("failed to create collectors with integrity check: %w", err) + } + + clusterCfg, err := cluster.GetClusterConfig(collectors, configPath) if err != nil { return instCfg, err } diff --git a/go.mod b/go.mod index a19ef783a..80eb4c976 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,6 @@ require ( github.com/tarantool/cartridge-cli v0.0.0-20220605082730-53e6a5be9a61 github.com/tarantool/go-prompt v1.0.0 github.com/tarantool/go-tarantool v1.10.1-0.20230309143354-e257ff30dd4d - github.com/tarantool/tt/cli/integrity v0.0.0-00010101000000-000000000000 github.com/vmihailenco/msgpack/v5 v5.3.5 github.com/yuin/gopher-lua v1.1.1-0.20230219103905-71163b697a8f go.etcd.io/etcd/api/v3 v3.5.9 @@ -103,5 +102,4 @@ require ( replace ( github.com/c-bata/go-prompt => github.com/tarantool/go-prompt v0.2.6-tarantool github.com/tarantool/cartridge-cli => ./cli/cartridge/third_party/cartridge-cli - github.com/tarantool/tt/cli/integrity => ./cli/integrity ) diff --git a/magefile.go b/magefile.go index 45507b4f7..021ccf8ff 100644 --- a/magefile.go +++ b/magefile.go @@ -47,10 +47,6 @@ var ( pythonExecutableName = "python3" ttExecutableName = "tt" - submodulePaths = []string{ - "./cli/integrity", - } - generateModePath = filepath.Join(packagePath, "codegen", "generate_code.go") ) @@ -251,37 +247,11 @@ func Unit() error { mg.Deps(GenerateGoCode) if mg.Verbose() { - err := sh.RunV(goExecutableName, "test", "-v", + return sh.RunV(goExecutableName, "test", "-v", fmt.Sprintf("%s/...", packagePath)) - if err != nil { - return err - } - - for _, submodulePath := range submodulePaths { - err := sh.RunV(goExecutableName, "test", "-v", - "-C", submodulePath, "./...") - if err != nil { - return err - } - } - - return nil - } - - err := sh.RunV(goExecutableName, "test", fmt.Sprintf("%s/...", packagePath)) - if err != nil { - return err } - for _, submodulePath := range submodulePaths { - err = sh.RunV(goExecutableName, "test", - "-C", submodulePath, "./...") - if err != nil { - return err - } - } - - return nil + return sh.RunV(goExecutableName, "test", fmt.Sprintf("%s/...", packagePath)) } // Run unit tests with a Tarantool instance integration.