2017-08-18 12 views
2

私は、基本的には様々なパスワードストア(Ansible Vault、Hashicorp Vault、Chef Vaultなど)のラッパーである小さなGoアプリケーションに取り組んでいます。アイデアは:私のさまざまなプロビジョニングスクリプトでは、Goラッパーを使って秘密を掴むことができます。パスワードストアをバックグラウンドで切り替えると、すべてのインターフェイスをプロジェクト全体で更新する必要はありません。依存性注入とテスト

私はこのアプリケーションの適切なテストをセットアップしようとしています。そうすることで、依存関係を挿入する最良の方法を見つけようとしています。

たとえば、プロジェクトの名前がsecretsであるとします。私の実装の1つはansibleです。また、実行可能な実装では、独自のparserが必要であり、データを取得するために、自身のconnectionを安全な格納域に開かなければなりません。

だから私は、次のものがあります:

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がそれほど知っていないようにこれを構造化するより論理的な方法がありますか?あるいは、トップレベルのパッケージがすべてを編成するのは典型的なことですか?

答えて

2

バックエンドとは何を決定しますか?それはあなたを導く助けになるはずです。私はこのプロジェクトで複数のデータベースをサポートしている同様のものを行って、そして私がやったことは、基本的だったしました:

  • configパッケージには、どのようなバックエンドが
  • storeパッケージはジェネリックを提供しています使用されて決定され、設定ファイルを読み込み、インターフェイスと設定を取る機能を有しており、実装に
  • serverパッケージ参照を返すだけインタフェース
  • mainパッケージは、設定を読み取るstoreでファクトリ関数に渡し、次に目を注入E作成

上のサーバになり、私は(実際にはデータストアを使用しています)私のサーバーを作成するときに、私は、インターフェイスを返しstoreの工場機能に設定を渡し、その後にそのを注入サーバ。さまざまな具体的な実装について知っておかなければならないのは、インターフェイスとファクトリを公開するパッケージと同じです。 serverconfig、およびmainパッケージはブラックボックスとして表示されます。

+0

ご返信ありがとうございます。はい、私は不可能な設定マップを持っています。新しい()メソッドはすべての設定データを持っています(おそらくファイルか何かから引っ張られたもの)。私の質問は、あなたが 'store'に何かを模倣したいのであれば、モックを提供する設定キーがありますか? – djt

+0

いいえ、私は完全にファクトリ関数をスキップし、テスト中のユニットに直接スタブ/モックを渡します。 – Adrian