package data_test import ( "fmt" "io/ioutil" "os" "path" "path/filepath" "strings" "testing" "github.com/ClickHouse/ClickHouse/programs/diagnostics/internal/platform/data" "github.com/stretchr/testify/require" ) func TestNextFileDirectoryFrame(t *testing.T) { t.Run("can iterate file frame", func(t *testing.T) { tempDir := t.TempDir() files := make([]string, 5) for i := 0; i < 5; i++ { fileDir := path.Join(tempDir, fmt.Sprintf("%d", i)) err := os.MkdirAll(fileDir, os.ModePerm) require.Nil(t, err) filepath := path.Join(fileDir, fmt.Sprintf("random-%d.txt", i)) files[i] = filepath _, err = os.Create(filepath) require.Nil(t, err) } fileFrame, errs := data.NewFileDirectoryFrame(tempDir, []string{"*.txt"}) require.Empty(t, errs) i := 0 for { values, ok, err := fileFrame.Next() require.Nil(t, err) if !ok { break } require.Len(t, values, 1) require.Equal(t, files[i], values[0].(data.SimpleFile).Path) i += 1 } require.Equal(t, 5, i) }) t.Run("can iterate file frame when empty", func(t *testing.T) { // create 5 temporary files tempDir := t.TempDir() fileFrame, errs := data.NewFileDirectoryFrame(tempDir, []string{"*"}) require.Empty(t, errs) i := 0 for { _, ok, err := fileFrame.Next() require.Nil(t, err) if !ok { break } } require.Equal(t, 0, i) }) } func TestNewConfigFileFrame(t *testing.T) { t.Run("can iterate config file frame", func(t *testing.T) { cwd, err := os.Getwd() require.Nil(t, err) configFrame, errs := data.NewConfigFileFrame(path.Join(cwd, "../../../testdata", "configs", "xml")) require.Empty(t, errs) i := 0 for { values, ok, err := configFrame.Next() require.Nil(t, err) if !ok { break } require.Len(t, values, 1) filePath := values[0].(data.XmlConfigFile).FilePath() require.True(t, strings.Contains(filePath, ".xml")) i += 1 } // 5 not 3 due to the includes require.Equal(t, 5, i) }) t.Run("can iterate file frame when empty", func(t *testing.T) { // create 5 temporary files tempDir := t.TempDir() configFrame, errs := data.NewConfigFileFrame(tempDir) require.Empty(t, errs) i := 0 for { _, ok, err := configFrame.Next() require.Nil(t, err) if !ok { break } } require.Equal(t, 0, i) }) } func TestConfigFileFrameCopy(t *testing.T) { t.Run("can copy non-sensitive xml config files", func(t *testing.T) { tmrDir := t.TempDir() cwd, err := os.Getwd() require.Nil(t, err) configFrame, errs := data.NewConfigFileFrame(path.Join(cwd, "../../../testdata", "configs", "xml")) require.Empty(t, errs) for { values, ok, err := configFrame.Next() require.Nil(t, err) if !ok { break } require.Nil(t, err) require.True(t, ok) configFile := values[0].(data.XmlConfigFile) newPath := path.Join(tmrDir, filepath.Base(configFile.FilePath())) err = configFile.Copy(newPath, false) require.FileExists(t, newPath) sourceInfo, _ := os.Stat(configFile.FilePath()) destInfo, _ := os.Stat(newPath) require.Equal(t, sourceInfo.Size(), destInfo.Size()) require.Nil(t, err) } }) t.Run("can copy sensitive xml config files", func(t *testing.T) { tmrDir := t.TempDir() cwd, err := os.Getwd() require.Nil(t, err) configFrame, errs := data.NewConfigFileFrame(path.Join(cwd, "../../../testdata", "configs", "xml")) require.Empty(t, errs) i := 0 var checkedFiles []string for { values, ok, err := configFrame.Next() require.Nil(t, err) if !ok { break } require.Nil(t, err) require.True(t, ok) configFile := values[0].(data.XmlConfigFile) fileName := filepath.Base(configFile.FilePath()) newPath := path.Join(tmrDir, fileName) err = configFile.Copy(newPath, true) require.FileExists(t, newPath) require.Nil(t, err) bytes, err := ioutil.ReadFile(newPath) require.Nil(t, err) s := string(bytes) checkedFiles = append(checkedFiles, fileName) if fileName == "users.xml" || fileName == "default-password.xml" || fileName == "user-include.xml" { require.True(t, strings.Contains(s, "Replaced") || strings.Contains(s, "Replaced")) require.NotContains(t, s, "REPLACE_ME") require.NotContains(t, s, "REPLACE_ME") } else if fileName == "config.xml" { require.True(t, strings.Contains(s, "Replaced")) require.True(t, strings.Contains(s, "Replaced")) require.True(t, strings.Contains(s, "Replaced")) require.NotContains(t, s, "REPLACE_ME") require.NotContains(t, s, "REPLACE_ME") require.NotContains(t, s, "REPLACE_ME") } i++ } require.ElementsMatch(t, []string{"users.xml", "default-password.xml", "user-include.xml", "config.xml", "server-include.xml"}, checkedFiles) require.Equal(t, 5, i) }) t.Run("can copy sensitive yaml config files", func(t *testing.T) { tmrDir := t.TempDir() cwd, err := os.Getwd() require.Nil(t, err) configFrame, errs := data.NewConfigFileFrame(path.Join(cwd, "../../../testdata", "configs", "yaml")) require.Empty(t, errs) i := 0 var checkedFiles []string for { values, ok, err := configFrame.Next() require.Nil(t, err) if !ok { break } require.Nil(t, err) require.True(t, ok) configFile := values[0].(data.YamlConfigFile) fileName := filepath.Base(configFile.FilePath()) newPath := path.Join(tmrDir, fileName) err = configFile.Copy(newPath, true) require.FileExists(t, newPath) require.Nil(t, err) bytes, err := ioutil.ReadFile(newPath) require.Nil(t, err) s := string(bytes) checkedFiles = append(checkedFiles, fileName) if fileName == "users.yaml" || fileName == "default-password.yaml" || fileName == "user-include.yaml" { require.True(t, strings.Contains(s, "password: 'Replaced'") || strings.Contains(s, "password_sha256_hex: 'Replaced'")) require.NotContains(t, s, "password: 'REPLACE_ME'") require.NotContains(t, s, "password_sha256_hex: \"REPLACE_ME\"") } else if fileName == "config.yaml" { require.True(t, strings.Contains(s, "access_key_id: 'Replaced'")) require.True(t, strings.Contains(s, "secret_access_key: 'Replaced'")) require.True(t, strings.Contains(s, "secret: 'Replaced'")) require.NotContains(t, s, "access_key_id: 'REPLACE_ME'") require.NotContains(t, s, "secret_access_key: REPLACE_ME") require.NotContains(t, s, "secret: REPLACE_ME") } i++ } require.ElementsMatch(t, []string{"users.yaml", "default-password.yaml", "user-include.yaml", "config.yaml", "server-include.yaml"}, checkedFiles) require.Equal(t, 5, i) }) } func TestConfigFileFrameFindLogPaths(t *testing.T) { t.Run("can find xml log paths", func(t *testing.T) { cwd, err := os.Getwd() require.Nil(t, err) configFrame, errs := data.NewConfigFileFrame(path.Join(cwd, "../../../testdata", "configs", "xml")) require.Empty(t, errs) paths, errs := configFrame.FindLogPaths() require.Empty(t, errs) require.ElementsMatch(t, []string{"/var/log/clickhouse-server/clickhouse-server.log", "/var/log/clickhouse-server/clickhouse-server.err.log"}, paths) }) t.Run("can handle empty log paths", func(t *testing.T) { configFrame, errs := data.NewConfigFileFrame(t.TempDir()) require.Empty(t, errs) paths, errs := configFrame.FindLogPaths() require.Empty(t, errs) require.Empty(t, paths) }) t.Run("can find yaml log paths", func(t *testing.T) { cwd, err := os.Getwd() require.Nil(t, err) configFrame, errs := data.NewConfigFileFrame(path.Join(cwd, "../../../testdata", "configs", "yaml")) require.Empty(t, errs) paths, errs := configFrame.FindLogPaths() require.Empty(t, errs) require.ElementsMatch(t, []string{"/var/log/clickhouse-server/clickhouse-server.log", "/var/log/clickhouse-server/clickhouse-server.err.log"}, paths) }) } // test the legacy format for ClickHouse xml config files with a yandex root tag func TestYandexConfigFile(t *testing.T) { t.Run("can find xml log paths with yandex root", func(t *testing.T) { cwd, err := os.Getwd() require.Nil(t, err) configFrame, errs := data.NewConfigFileFrame(path.Join(cwd, "../../../testdata", "configs", "yandex_xml")) require.Empty(t, errs) paths, errs := configFrame.FindLogPaths() require.Empty(t, errs) require.ElementsMatch(t, []string{"/var/log/clickhouse-server/clickhouse-server.log", "/var/log/clickhouse-server/clickhouse-server.err.log"}, paths) }) }