【发布时间】:2018-01-27 14:13:34
【问题描述】:
我正在开发一个小型 Go 应用程序,它基本上是各种密码存储(Ansible Vault、Hashicorp Vault、Chef Vault 等)的包装器。这个想法是:在我的各种配置脚本中,我可以使用我的 Go 包装器来获取秘密,如果我们决定在幕后切换密码存储,则不需要在我的项目中更新所有接口。
我正在尝试为此应用程序设置适当的测试,并在此过程中试图找出注入我的依赖项的最佳方法。
例如,假设项目名为secrets。我的实现之一是ansible。而ansible实现需要自己的parser,需要打开自己的connection到ansible vault,才能取回数据。
所以我可能有以下几点:
package secrets
type PasswordStore interface {
GetKey(key string) (string, error)
}
func New(backend string, config map[string]interface{}) (PasswordStore, error) {
switch backend {
case "ansible":
return ansible.New(config)
default:
return nil, fmt.Errorf("Password store '%s' not supported.", backend)
}
}
package ansible
type Connection interface {
open() (string, error)
}
type Ansible struct {
connection Connection
contents map[string]string
}
func New(c map[string]interface{}) (*Ansible, error) {
conn, err := NewConnection(c["ansible_path"].(string))
if err != nil {
return nil, err
}
// open connection, parse, etc...
a := &Ansible{
connection: conn,
contents: parsedData,
}
return a, nil
}
所以这看起来不错,因为secrets 包不需要了解ansible 包依赖项(连接),并且工厂只是新建了带有一些配置数据的实例。但是,如果我需要模拟 Ansible 收到的 connection,似乎没有一个好的方法可以做到这一点(除非配置映射有一个名为 mock 的连接选项)
另一种选择是放弃工厂,只组装secrets包中的所有依赖项,例如:
package secrets
type PasswordStore interface {
GetKey(key string) (string, error)
}
func New(backend string, config map[string]interface{}) (PasswordStore, error) {
switch backend {
case "ansible":
return ansible.New(AnsibleConnection{}, config)
default:
return nil, fmt.Errorf("Password store '%s' not supported.", backend)
}
}
package ansible
// same as before in this file, but with injected dependency ...
func New(connect Connection, c map[string]interface{}) (*Ansible, error) {
conn, err := connect.NewConnection(c["ansible_path"].(string))
if err != nil {
return nil, err
}
// open connection, parse, etc...
a := &Ansible{
connection: conn,
contents: parsedData,
}
return a, nil
}
现在注入了依赖项,但似乎secrets 需要了解每个实现的每个依赖项。
有没有更合乎逻辑的方式来构建这个,让secrets 知道的更少?还是顶级包通常会编排所有内容?
【问题讨论】:
标签: go