[インデックス 18047] ファイルの概要
このコミットは、Go言語のnet
パッケージにおけるDNS設定の読み込みに関するテストの追加と、それに伴う既存コードの修正を扱っています。具体的には、dnsconfig_unix.go
で定義されているDNS設定読み込み関数dnsReadConfig
が、設定ファイルパスを引数として受け取るように変更され、それに対応するテストがdnsconfig_unix_test.go
として新規追加されています。また、テスト用のresolv.conf
サンプルファイルもtestdata/resolv.conf
として追加されています。
コミット
commit b3f38b463023e149f2906b85c2988854d10bcab4
Author: Anfernee Yongkun Gui <anfernee.gui@gmail.com>
Date: Wed Dec 18 08:26:36 2013 -0800
net: test dnsconfig_unix with sample resolv.conf
R=golang-dev, adg, bradfitz, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/21580043
---
src/pkg/net/dnsclient_unix.go | 3 ++-
src/pkg/net/dnsconfig_unix.go | 5 ++--
src/pkg/net/dnsconfig_unix_test.go | 48 ++++++++++++++++++++++++++++++++++++++
src/pkg/net/testdata/resolv.conf | 5 ++++\n 4 files changed, 57 insertions(+), 4 deletions(-)
diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go
index 16cf420dcd..a30c9a73d7 100644
--- a/src/pkg/net/dnsclient_unix.go
+++ b/src/pkg/net/dnsclient_unix.go
@@ -159,7 +159,8 @@ func convertRR_AAAA(records []dnsRR) []IP {
var cfg *dnsConfig
var dnserr error
-func loadConfig() { cfg, dnserr = dnsReadConfig() }\n+// Assume dns config file is /etc/resolv.conf here
+func loadConfig() { cfg, dnserr = dnsReadConfig("/etc/resolv.conf") }\n
var onceLoadConfig sync.Once
diff --git a/src/pkg/net/dnsconfig_unix.go b/src/pkg/net/dnsconfig_unix.go
index d10f099b12..7856ebc80d 100644
--- a/src/pkg/net/dnsconfig_unix.go
+++ b/src/pkg/net/dnsconfig_unix.go
@@ -20,9 +20,8 @@ type dnsConfig struct {
// See resolv.conf(5) on a Linux machine.
// TODO(rsc): Supposed to call uname() and chop the beginning
// of the host name to get the default search domain.
-// We assume it's in resolv.conf anyway.\n-func dnsReadConfig() (*dnsConfig, error) {\n-\tfile, err := open("/etc/resolv.conf")\n+func dnsReadConfig(filename string) (*dnsConfig, error) {\n+\tfile, err := open(filename)\n if err != nil {
return nil, &DNSConfigError{err}
}\ndiff --git a/src/pkg/net/dnsconfig_unix_test.go b/src/pkg/net/dnsconfig_unix_test.go
new file mode 100644
index 0000000000..b24291c1c2
--- /dev/null
+++ b/src/pkg/net/dnsconfig_unix_test.go
@@ -0,0 +1,48 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package net
+
+import (
+ "testing"
+)
+
+func TestReadConfig(t *testing.T) {
+ dnsConfig, err := dnsReadConfig("testdata/resolv.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(dnsConfig.servers) != 1 {
+ t.Errorf("len(dnsConfig.servers) = %d; want %d", len(dnsConfig.servers), 1)
+ }
+ if dnsConfig.servers[0] != "[192.168.1.1]" {
+ t.Errorf("dnsConfig.servers[0] = %s; want %s", dnsConfig.servers[0], "[192.168.1.1]")
+ }
+
+ if len(dnsConfig.search) != 1 {
+ t.Errorf("len(dnsConfig.search) = %d; want %d", len(dnsConfig.search), 1)
+ }
+ if dnsConfig.search[0] != "Home" {
+ t.Errorf("dnsConfig.search[0] = %s; want %s", dnsConfig.search[0], "Home")
+ }
+
+ if dnsConfig.ndots != 5 {
+ t.Errorf("dnsConfig.ndots = %d; want %d", dnsConfig.ndots, 5)
+ }
+
+ if dnsConfig.timeout != 10 {
+ t.Errorf("dnsConfig.timeout = %d; want %d", dnsConfig.timeout, 10)
+ }
+
+ if dnsConfig.attempts != 3 {
+ t.Errorf("dnsConfig.attempts = %d; want %d", dnsConfig.attempts, 3)
+ }
+
+ if dnsConfig.rotate != true {
+ t.Errorf("dnsConfig.rotate = %t; want %t", dnsConfig.rotate, true)
+ }
+}
diff --git a/src/pkg/net/testdata/resolv.conf b/src/pkg/net/testdata/resolv.conf
new file mode 100644
index 0000000000..b5972e09c9
--- /dev/null
+++ b/src/pkg/net/testdata/resolv.conf
@@ -0,0 +1,5 @@
+# /etc/resolv.conf
+
+domain Home
+nameserver 192.168.1.1
+options ndots:5 timeout:10 attempts:3 rotate
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b3f38b463023e149f2906b85c2988854d10bcab4
元コミット内容
このコミットの目的は、Go言語のnet
パッケージにおけるDNS設定の読み込みロジック、特にUnix系システムにおけるresolv.conf
ファイルのパース処理に対して、より堅牢なテストカバレッジを提供することです。
具体的には、以下の変更が行われています。
dnsReadConfig
関数の引数化: 以前は/etc/resolv.conf
という固定パスからDNS設定を読み込んでいたdnsReadConfig
関数が、ファイルパスを引数として受け取るように変更されました。これにより、テスト時に任意のresolv.conf
ファイルを指定して、そのパース結果を検証することが可能になります。loadConfig
関数の修正:dnsReadConfig
の変更に伴い、loadConfig
関数も/etc/resolv.conf
を明示的に指定してdnsReadConfig
を呼び出すように修正されました。これにより、本番環境での動作は維持されます。- 新規テストファイルの追加:
src/pkg/net/dnsconfig_unix_test.go
が新規作成され、dnsReadConfig
関数のテストケースが追加されました。このテストは、特定のresolv.conf
ファイル(testdata/resolv.conf
)を読み込み、その内容が正しくパースされているかを確認します。 - テスト用
resolv.conf
の追加:src/pkg/net/testdata/resolv.conf
が新規作成されました。このファイルは、domain
,nameserver
,options
といった一般的なresolv.conf
のエントリを含むサンプルデータであり、テストの入力として使用されます。
これらの変更により、Goのnet
パッケージがresolv.conf
ファイルをどのように解釈し、DNS設定を構築するかについて、より詳細なテストが可能になり、将来的なバグの混入を防ぐことに貢献します。
変更の背景
このコミットの背景には、Go言語のネットワークスタック、特にDNSリゾルバの堅牢性を向上させるという目的があります。resolv.conf
ファイルはUnix系システムにおけるDNSクライアントの設定を司る重要なファイルであり、そのパースロジックにバグがあると、DNSルックアップが失敗したり、予期せぬ動作を引き起こしたりする可能性があります。
以前のdnsReadConfig
関数は、/etc/resolv.conf
という固定パスから設定を読み込んでいました。これは実際の運用環境では問題ありませんが、テストの観点からは柔軟性に欠けていました。特定のresolv.conf
のパターン(例えば、異なるoptions
設定や複数のnameserver
エントリなど)に対するパースの正確性を検証するためには、テストごとに/etc/resolv.conf
を書き換える必要があり、これは非効率的で、テスト環境の汚染にもつながります。
このコミットは、dnsReadConfig
関数を引数化することで、テスト時に任意のresolv.conf
ファイルを指定できるようにし、これにより、様々なresolv.conf
のシナリオに対する網羅的なテストを容易にすることを目的としています。これにより、GoのDNSリゾルバが多様なシステム環境下で正しく動作することを保証し、信頼性を向上させることが可能になります。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
1. DNS (Domain Name System)
DNSは、インターネット上のコンピュータやサービスを識別するための階層的な分散型命名システムです。人間が覚えやすいドメイン名(例: google.com
)を、コンピュータが理解できるIPアドレス(例: 172.217.160.142
)に変換する役割を担っています。
2. resolv.conf
ファイル
Unix系オペレーティングシステム(Linux, macOS, BSDなど)において、DNSクライアント(リゾルバ)の設定を記述するファイルです。通常、/etc/resolv.conf
に配置されます。このファイルには、主に以下の情報が記述されます。
nameserver
: DNSクエリを送信するDNSサーバーのIPアドレスを指定します。複数指定することも可能です。domain
: ローカルドメイン名を指定します。ホスト名が完全修飾ドメイン名(FQDN)でない場合に、このドメイン名が自動的に付加されます。search
: ドメイン名の検索リストを指定します。ホスト名がFQDNでない場合に、このリストに指定されたドメイン名が順番に付加されてDNSルックアップが試行されます。options
: リゾルバの動作に関する様々なオプションを指定します。例えば、ndots
: ドメイン名にピリオドがいくつ含まれていればFQDNとみなすか(それ以下の場合、search
リストが適用される)。timeout
: DNSクエリのタイムアウト時間(秒)。attempts
: DNSクエリの再試行回数。rotate
: 複数のnameserver
が指定されている場合に、ラウンドロビン方式でDNSサーバーを使用するかどうか。
3. Go言語のnet
パッケージ
Go言語の標準ライブラリであるnet
パッケージは、ネットワークI/Oのプリミティブを提供します。これには、TCP/UDPソケット、IPアドレスの操作、DNSルックアップなどが含まれます。このコミットで変更されているのは、net
パッケージ内のDNSリゾルバの実装の一部です。
4. ユニットテスト
ソフトウェア開発において、個々のプログラムの部品(関数やメソッドなど)が正しく動作するかを検証するテスト手法です。このコミットでは、dnsReadConfig
関数がresolv.conf
ファイルを正しくパースできるかを検証するためのユニットテストが追加されています。テストの際には、実際のシステムファイルではなく、テスト用に用意されたサンプルファイルを使用することで、テストの独立性と再現性を高めます。
5. sync.Once
Go言語のsync
パッケージに含まれる型で、特定の処理が一度だけ実行されることを保証するために使用されます。このコミットでは、loadConfig
関数が一度だけ実行されるようにsync.Once
が使用されています。これは、DNS設定の読み込みがアプリケーションのライフサイクル中に一度だけ行われるべき処理であるためです。
技術的詳細
このコミットにおける技術的な変更点は、主にdnsReadConfig
関数のインターフェース変更と、それに対応するテストの追加に集約されます。
dnsReadConfig
関数の変更
変更前:
func dnsReadConfig() (*dnsConfig, error) {
file, err := open("/etc/resolv.conf")
// ...
}
変更後:
func dnsReadConfig(filename string) (*dnsConfig, error) {
file, err := open(filename)
// ...
}
この変更により、dnsReadConfig
関数はハードコードされていた/etc/resolv.conf
のパスではなく、filename
という引数を通じて任意のファイルパスを受け取るようになりました。これにより、以下のメリットが生まれます。
- テスト容易性の向上: テストコードから、実際の
/etc/resolv.conf
ファイルを変更することなく、様々な内容のresolv.conf
ファイルを指定してdnsReadConfig
の動作を検証できるようになります。これは、モックやスタブを使用せずに、実際のファイルI/Oを伴うテストを可能にする点で重要です。 - 柔軟性の向上: 将来的に、
resolv.conf
以外の場所からDNS設定を読み込む必要がある場合や、複数のDNS設定ファイルを切り替えて使用するようなシナリオが発生した場合にも、この関数を再利用しやすくなります。
loadConfig
関数の変更
変更前:
func loadConfig() { cfg, dnserr = dnsReadConfig() }
変更後:
// Assume dns config file is /etc/resolv.conf here
func loadConfig() { cfg, dnserr = dnsReadConfig("/etc/resolv.conf") }
dnsReadConfig
のインターフェース変更に伴い、loadConfig
関数も修正されました。loadConfig
は、Goのnet
パッケージがDNS設定を初期化する際に一度だけ呼び出される関数です。この関数内では、引き続き本番環境での標準的なresolv.conf
のパスである/etc/resolv.conf
が明示的に指定されています。これにより、dnsReadConfig
の引数化が、既存のアプリケーションの動作に影響を与えないように配慮されています。
dnsconfig_unix_test.go
の新規追加
このコミットの最も重要な部分の一つは、dnsconfig_unix_test.go
という新しいテストファイルの追加です。このファイルには、TestReadConfig
というテスト関数が含まれています。
func TestReadConfig(t *testing.T) {
dnsConfig, err := dnsReadConfig("testdata/resolv.conf")
if err != nil {
t.Fatal(err)
}
// 各フィールドの検証
if len(dnsConfig.servers) != 1 {
t.Errorf("len(dnsConfig.servers) = %d; want %d", len(dnsConfig.servers), 1)
}
if dnsConfig.servers[0] != "[192.168.1.1]" {
t.Errorf("dnsConfig.servers[0] = %s; want %s", dnsConfig.servers[0], "[192.168.1.1]")
}
// ... (他のフィールドの検証)
}
このテストは、以下の手順でdnsReadConfig
の動作を検証します。
dnsReadConfig("testdata/resolv.conf")
を呼び出し、テスト用のresolv.conf
ファイルを読み込みます。- エラーが発生した場合は、テストを失敗させます。
- 返された
dnsConfig
構造体の各フィールド(servers
,search
,ndots
,timeout
,attempts
,rotate
)が、testdata/resolv.conf
の内容と一致するかをアサートします。
このテストにより、dnsReadConfig
関数がresolv.conf
の様々なエントリを正しくパースし、対応するdnsConfig
構造体のフィールドにマッピングできることが保証されます。
testdata/resolv.conf
の新規追加
テストの入力として使用されるtestdata/resolv.conf
ファイルは、以下の内容を含んでいます。
# /etc/resolv.conf
domain Home
nameserver 192.168.1.1
options ndots:5 timeout:10 attempts:3 rotate
このファイルは、resolv.conf
で一般的に使用されるdomain
, nameserver
, options
ディレクティブを含んでおり、dnsReadConfig
がこれらの設定を正しく解釈できるかを検証するための具体的なシナリオを提供します。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下の3つのファイルです。
-
src/pkg/net/dnsclient_unix.go
:loadConfig
関数のシグネチャが変更され、dnsReadConfig
を呼び出す際に/etc/resolv.conf
という固定パスを引数として渡すように修正されました。
-
src/pkg/net/dnsconfig_unix.go
:dnsReadConfig
関数のシグネチャが変更され、引数としてfilename string
を受け取るようになりました。これにより、読み込むresolv.conf
ファイルのパスを外部から指定できるようになりました。open
関数に渡すファイルパスが、ハードコードされた/etc/resolv.conf
から引数filename
に変更されました。
-
src/pkg/net/dnsconfig_unix_test.go
:- 新規作成されたファイルです。
TestReadConfig
関数が定義され、testdata/resolv.conf
を読み込み、そのパース結果を検証するユニットテストが含まれています。
-
src/pkg/net/testdata/resolv.conf
:- 新規作成されたファイルです。
TestReadConfig
で使用されるサンプルresolv.conf
ファイルの内容が記述されています。
コアとなるコードの解説
src/pkg/net/dnsconfig_unix.go
におけるdnsReadConfig
の変更
// 変更前:
// func dnsReadConfig() (*dnsConfig, error) {
// file, err := open("/etc/resolv.conf")
// 変更後:
func dnsReadConfig(filename string) (*dnsConfig, error) {
file, err := open(filename)
if err != nil {
return nil, &DNSConfigError{err}
}
// ... (以降のパースロジックは変更なし)
}
この変更は、dnsReadConfig
関数の汎用性を高めるためのものです。以前は常に/etc/resolv.conf
を読み込んでいましたが、filename
引数を導入することで、任意のパスにあるresolv.conf
ファイルを読み込めるようになりました。これは、特にテストにおいて、様々なresolv.conf
のシナリオをシミュレートするために不可欠な変更です。
src/pkg/net/dnsclient_unix.go
におけるloadConfig
の変更
// 変更前:
// func loadConfig() { cfg, dnserr = dnsReadConfig() }
// 変更後:
// Assume dns config file is /etc/resolv.conf here
func loadConfig() { cfg, dnserr = dnsReadConfig("/etc/resolv.conf") }
dnsReadConfig
のインターフェース変更に伴い、loadConfig
関数も更新されました。loadConfig
は、GoのネットワークスタックがDNS設定を初期化する際に一度だけ呼び出される関数です。この変更により、本番環境では引き続き/etc/resolv.conf
がデフォルトのDNS設定ファイルとして使用されることが保証されます。コメントで「Assume dns config file is /etc/resolv.conf here」と明記されているのは、この関数の役割が/etc/resolv.conf
を読み込むことであることを示しています。
src/pkg/net/dnsconfig_unix_test.go
の新規追加とTestReadConfig
// +build darwin dragonfly freebsd linux netbsd openbsd
package net
import (
"testing"
)
func TestReadConfig(t *testing.T) {
dnsConfig, err := dnsReadConfig("testdata/resolv.conf")
if err != nil {
t.Fatal(err)
}
// 以下、dnsConfigの各フィールドが期待値と一致するかを検証
if len(dnsConfig.servers) != 1 {
t.Errorf("len(dnsConfig.servers) = %d; want %d", len(dnsConfig.servers), 1)
}
if dnsConfig.servers[0] != "[192.168.1.1]" {
t.Errorf("dnsConfig.servers[0] = %s; want %s", dnsConfig.servers[0], "[192.168.1.1]")
}
if len(dnsConfig.search) != 1 {
t.Errorf("len(dnsConfig.search) = %d; want %d", len(dnsConfig.search), 1)
}
if dnsConfig.search[0] != "Home" {
t.Errorf("dnsConfig.search[0] = %s; want %s", dnsConfig.search[0], "Home")
}
if dnsConfig.ndots != 5 {
t.Errorf("dnsConfig.ndots = %d; want %d", dnsConfig.ndots, 5)
}
if dnsConfig.timeout != 10 {
t.Errorf("dnsConfig.timeout = %d; want %d", dnsConfig.timeout, 10)
}
if dnsConfig.attempts != 3 {
t.Errorf("dnsConfig.attempts = %d; want %d", dnsConfig.attempts, 3)
}
if dnsConfig.rotate != true {
t.Errorf("dnsConfig.rotate = %t; want %t", dnsConfig.rotate, true)
}
}
このテストファイルは、dnsReadConfig
関数が正しく動作することを保証するためのものです。+build
ディレクティブは、このテストがUnix系システム(Darwin, Dragonfly, FreeBSD, Linux, NetBSD, OpenBSD)でのみビルドおよび実行されることを示しています。
TestReadConfig
関数は、dnsReadConfig
に"testdata/resolv.conf"
というパスを渡して呼び出します。これにより、実際のシステムファイルではなく、テスト用に用意されたサンプルファイルが読み込まれます。その後、dnsConfig
構造体の各フィールド(servers
, search
, ndots
, timeout
, attempts
, rotate
)が、testdata/resolv.conf
に記述された期待値と一致するかをt.Errorf
やt.Fatal
を使って検証しています。これにより、resolv.conf
のパースロジックにバグがないことを確認できます。
src/pkg/net/testdata/resolv.conf
の新規追加
# /etc/resolv.conf
domain Home
nameserver 192.168.1.1
options ndots:5 timeout:10 attempts:3 rotate
このファイルは、TestReadConfig
の入力として使用されるサンプルresolv.conf
です。コメント行、domain
、nameserver
、そして複数のオプションを含むoptions
行が含まれており、dnsReadConfig
がこれらの要素を正しく解釈できるかをテストするための具体的なデータを提供します。
関連リンク
- Go言語の
net
パッケージに関する公式ドキュメント: https://pkg.go.dev/net resolv.conf
に関するmanページ(Linuxの場合):man resolv.conf
(ターミナルで実行)- Go言語のテストに関する公式ドキュメント: https://go.dev/doc/tutorial/add-a-test
参考にした情報源リンク
resolv.conf
のmanページ(オンライン版の例): https://man7.org/linux/man-pages/man5/resolv.conf.5.html- Go言語の
sync.Once
に関するドキュメント: https://pkg.go.dev/sync#Once - Go言語のテストフレームワーク
testing
に関するドキュメント: https://pkg.go.dev/testing - Go言語のコミットガイドライン(一般的な情報源として): https://go.dev/doc/contribute#commit_messages
- Go言語の
net
パッケージのソースコード(変更前後の比較): https://github.com/golang/go/tree/master/src/net - Go言語の
+build
タグに関する情報: https://pkg.go.dev/cmd/go#hdr-Build_constraints