diff --git a/cmd/karma/assets.go b/cmd/karma/assets.go index d40ff1404..70fc9c887 100644 --- a/cmd/karma/assets.go +++ b/cmd/karma/assets.go @@ -2,9 +2,9 @@ package main import ( "io" - "io/ioutil" "mime" "net/http" + "os" "path/filepath" "strings" "time" @@ -26,7 +26,7 @@ func serveFileOr404(path string, contentType string) http.HandlerFunc { _, _ = w.Write([]byte{}) return } - data, err := ioutil.ReadFile(path) + data, err := os.ReadFile(path) if err != nil { contentText(w) w.WriteHeader(http.StatusNotFound) diff --git a/cmd/karma/benchmark_test.go b/cmd/karma/benchmark_test.go index 51da00201..191dc538e 100644 --- a/cmd/karma/benchmark_test.go +++ b/cmd/karma/benchmark_test.go @@ -1,9 +1,10 @@ package main import ( + "bytes" "fmt" - "io/ioutil" "net/http/httptest" + "os" "runtime" "testing" @@ -23,14 +24,14 @@ func reportMemoryMetrics(b *testing.B) { func BenchmarkCompress(b *testing.B) { zerolog.SetGlobalLevel(zerolog.FatalLevel) - data, err := ioutil.ReadFile("./tests/compress/alerts.json") + data, err := os.ReadFile("./tests/compress/alerts.json") if err != nil { b.Errorf("Failed to read data: %s", err.Error()) } b.Run("Run", func(b *testing.B) { for i := 0; i < b.N; i++ { - compressed, err := compressResponse(data) + compressed, err := compressResponse(data, nil) if err != nil { b.Errorf("Failed to compress data: %s", err.Error()) } @@ -48,19 +49,19 @@ func BenchmarkCompress(b *testing.B) { func BenchmarkDecompress(b *testing.B) { zerolog.SetGlobalLevel(zerolog.FatalLevel) - data, err := ioutil.ReadFile("./tests/compress/alerts.json") + data, err := os.ReadFile("./tests/compress/alerts.json") if err != nil { b.Errorf("Failed to read data: %s", err.Error()) } - compressed, err := compressResponse(data) + compressed, err := compressResponse(data, nil) if err != nil { b.Errorf("Failed to compress data: %s", err.Error()) } b.Run("Run", func(b *testing.B) { for i := 0; i < b.N; i++ { - _, err := decompressCachedResponse(compressed) + _, err := decompressCachedResponse(bytes.NewReader(compressed)) if err != nil { b.Errorf("Failed to decompress data: %s", err.Error()) } @@ -75,19 +76,19 @@ func BenchmarkDecompress(b *testing.B) { func BenchmarkCompressionAndDecompression(b *testing.B) { zerolog.SetGlobalLevel(zerolog.FatalLevel) - data, err := ioutil.ReadFile("./tests/compress/alerts.json") + data, err := os.ReadFile("./tests/compress/alerts.json") if err != nil { b.Errorf("Failed to read data: %s", err.Error()) } b.Run("Run", func(b *testing.B) { for i := 0; i < b.N; i++ { - compressed, err := compressResponse(data) + compressed, err := compressResponse(data, nil) if err != nil { b.Errorf("Failed to compress data: %s", err.Error()) } - _, err = decompressCachedResponse(compressed) + _, err = decompressCachedResponse(bytes.NewReader(compressed)) if err != nil { b.Errorf("Failed to decompress data: %s", err.Error()) } diff --git a/cmd/karma/main.go b/cmd/karma/main.go index a463e28d5..2df98aa6a 100644 --- a/cmd/karma/main.go +++ b/cmd/karma/main.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "html/template" - "io/ioutil" "mime" "net" "net/http" @@ -404,7 +403,7 @@ func writePidFile() error { if pidFile != "" { log.Info().Str("path", pidFile).Msg("Writing PID file") pid := os.Getpid() - err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(pid)), 0644) + err := os.WriteFile(pidFile, []byte(strconv.Itoa(pid)), 0644) if err != nil { return fmt.Errorf("failed to write a PID file: %s", err) } diff --git a/cmd/karma/proxy.go b/cmd/karma/proxy.go index 73c6a0a5d..82d8ae98b 100644 --- a/cmd/karma/proxy.go +++ b/cmd/karma/proxy.go @@ -3,7 +3,7 @@ package main import ( "bytes" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httputil" "net/url" @@ -85,7 +85,7 @@ func handlePostRequest(alertmanager *alertmanager.Alertmanager, h http.Handler) Msg("Proxy request") defer r.Body.Close() - body, err := ioutil.ReadAll(r.Body) + body, err := io.ReadAll(r.Body) if err != nil { log.Error().Err(err). Str("alertmanager", alertmanager.Name). @@ -153,11 +153,11 @@ func handlePostRequest(alertmanager *alertmanager.Alertmanager, h http.Handler) return } - r.Body = ioutil.NopCloser(bytes.NewBuffer(newBody)) + r.Body = io.NopCloser(bytes.NewBuffer(newBody)) r.ContentLength = int64(len(newBody)) r.Header.Set("Content-Length", fmt.Sprintf("%d", r.ContentLength)) } else { - r.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + r.Body = io.NopCloser(bytes.NewBuffer(body)) } h.ServeHTTP(w, r) diff --git a/cmd/karma/proxy_test.go b/cmd/karma/proxy_test.go index a78516087..685d224e3 100644 --- a/cmd/karma/proxy_test.go +++ b/cmd/karma/proxy_test.go @@ -4,7 +4,7 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "regexp" @@ -535,11 +535,11 @@ func TestProxyUserRewrite(t *testing.T) { _ = am.Pull() httpmock.RegisterResponder("POST", "http://localhost/api/v2/silences", func(req *http.Request) (*http.Response, error) { - body, _ := ioutil.ReadAll(req.Body) + body, _ := io.ReadAll(req.Body) return httpmock.NewBytesResponse(200, body), nil }) - req := httptest.NewRequest("POST", "/proxy/alertmanager/proxyAuth/api/v2/silences", ioutil.NopCloser(bytes.NewBufferString(testCase.frontednRequestBody))) + req := httptest.NewRequest("POST", "/proxy/alertmanager/proxyAuth/api/v2/silences", io.NopCloser(bytes.NewBufferString(testCase.frontednRequestBody))) for k, v := range testCase.requestHeaders { req.Header.Set(k, v) } @@ -551,7 +551,7 @@ func TestProxyUserRewrite(t *testing.T) { t.Errorf("Got response code %d instead of %d", resp.Code, testCase.responseCode) } - gotBody, _ := ioutil.ReadAll(resp.Body) + gotBody, _ := io.ReadAll(resp.Body) if string(gotBody) != testCase.proxyRequestBody { diff := difflib.UnifiedDiff{ A: difflib.SplitLines(testCase.proxyRequestBody), @@ -1193,11 +1193,11 @@ func TestProxySilenceACL(t *testing.T) { _ = am.Pull() httpmock.RegisterResponder("POST", "http://localhost/api/v2/silences", func(req *http.Request) (*http.Response, error) { - body, _ := ioutil.ReadAll(req.Body) + body, _ := io.ReadAll(req.Body) return httpmock.NewBytesResponse(200, body), nil }) - req := httptest.NewRequest("POST", "/proxy/alertmanager/proxyACL/api/v2/silences", ioutil.NopCloser(bytes.NewBufferString(testCase.frontednRequestBody))) + req := httptest.NewRequest("POST", "/proxy/alertmanager/proxyACL/api/v2/silences", io.NopCloser(bytes.NewBufferString(testCase.frontednRequestBody))) req.Header.Set("X-User", testCase.requestUsername) resp := newCloseNotifyingRecorder() @@ -1246,7 +1246,7 @@ func TestProxyRequestReadFailure(t *testing.T) { t.Errorf("Got response code %d instead of 500", resp.Code) } - gotBody, _ := ioutil.ReadAll(resp.Body) + gotBody, _ := io.ReadAll(resp.Body) if string(gotBody) != "request read error\n" { t.Errorf("Body mismatch:\n%s", gotBody) } @@ -1299,7 +1299,7 @@ func TestProxyRequestToUnsupportedAlertmanager(t *testing.T) { return } - req := httptest.NewRequest("POST", "/proxy/alertmanager/proxyToUnsupported/api/v2/silences", ioutil.NopCloser(bytes.NewBufferString(`{}`))) + req := httptest.NewRequest("POST", "/proxy/alertmanager/proxyToUnsupported/api/v2/silences", io.NopCloser(bytes.NewBufferString(`{}`))) resp := newCloseNotifyingRecorder() r.ServeHTTP(resp, req) @@ -1307,7 +1307,7 @@ func TestProxyRequestToUnsupportedAlertmanager(t *testing.T) { t.Errorf("Got response code %d instead of 500", resp.Code) } - gotBody, _ := ioutil.ReadAll(resp.Body) + gotBody, _ := io.ReadAll(resp.Body) if string(gotBody) != "can't find silence mapper for Alertmanager 0.1.0\n" { t.Errorf("Body mismatch:\n%s", gotBody) } diff --git a/cmd/karma/tests/testscript/096_tls_client.txt b/cmd/karma/tests/testscript/096_tls_client.txt index 4506121a9..1d722d653 100644 --- a/cmd/karma/tests/testscript/096_tls_client.txt +++ b/cmd/karma/tests/testscript/096_tls_client.txt @@ -92,7 +92,6 @@ import ( "crypto/tls" "crypto/x509" "io" - "io/ioutil" "log" "net" "net/http" @@ -136,7 +135,7 @@ func empty(w http.ResponseWriter, r *http.Request) { func main() { pid := os.Getpid() - err := ioutil.WriteFile("alertmanager.pid", []byte(strconv.Itoa(pid)), 0644) + err := os.WriteFile("alertmanager.pid", []byte(strconv.Itoa(pid)), 0644) if err != nil { log.Fatal(err) } @@ -146,7 +145,7 @@ func main() { http.HandleFunc("/api/v2/silences", empty) http.HandleFunc("/api/v2/alerts/groups", empty) - caCert, err := ioutil.ReadFile("ca.pem") + caCert, err := os.ReadFile("ca.pem") if err != nil { log.Fatal(err) } diff --git a/cmd/karma/views.go b/cmd/karma/views.go index 05033464a..087bc643d 100644 --- a/cmd/karma/views.go +++ b/cmd/karma/views.go @@ -6,7 +6,7 @@ import ( "encoding/base64" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "sort" "strings" @@ -45,10 +45,13 @@ func robots(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("User-agent: *\nDisallow: /\n")) } -func compressResponse(data []byte) ([]byte, error) { +func compressResponse(data []byte, gz io.WriteCloser) ([]byte, error) { var b bytes.Buffer - // this only fails if we pass unsupported level (3 is valid) - gz, _ := gzip.NewWriterLevel(&b, 3) + + if gz == nil { + // this only fails if we pass unsupported level (3 is valid) + gz, _ = gzip.NewWriterLevel(&b, 3) + } _, err := gz.Write(data) if err != nil { @@ -70,16 +73,16 @@ func compressResponse(data []byte) ([]byte, error) { return compressed, nil } -func decompressCachedResponse(data []byte) ([]byte, error) { - b := bytes.NewReader(data) - z, err := gzip.NewReader(b) +func decompressCachedResponse(r io.Reader) ([]byte, error) { + z, err := gzip.NewReader(r) if err != nil { return nil, fmt.Errorf("failed to created new compression reader: %s", err.Error()) } - p, err := ioutil.ReadAll(z) + p, err := io.ReadAll(z) if err != nil { return nil, fmt.Errorf("failed to decompress data: %s", err.Error()) } + z.Close() return p, nil } @@ -188,7 +191,8 @@ func alerts(w http.ResponseWriter, r *http.Request) { data, found := apiCache.Get(cacheKey) if found { - rawData, _ := decompressCachedResponse(data.([]byte)) + r := bytes.NewReader(data.([]byte)) + rawData, _ := decompressCachedResponse(r) // need to overwrite settings as they can have user specific data newResp := models.AlertsResponse{} _ = json.Unmarshal(rawData, &newResp) @@ -483,7 +487,7 @@ func alerts(w http.ResponseWriter, r *http.Request) { resp.Receivers = receivers data, _ = json.Marshal(resp) - compressedData, _ := compressResponse(data.([]byte)) + compressedData, _ := compressResponse(data.([]byte), nil) apiCache.Set(cacheKey, compressedData, -1) mimeJSON(w) diff --git a/cmd/karma/views_test.go b/cmd/karma/views_test.go index e0f17f825..14bc2810f 100644 --- a/cmd/karma/views_test.go +++ b/cmd/karma/views_test.go @@ -1,8 +1,10 @@ package main import ( + "bytes" "context" "encoding/json" + "errors" "fmt" "net/http" "net/http/httptest" @@ -2574,3 +2576,76 @@ func TestAlertFilters(t *testing.T) { } } } + +type gzErrWriter struct { + failWrite bool + failClose bool +} + +func (ew *gzErrWriter) Write(p []byte) (n int, err error) { + if ew.failWrite { + return 0, errors.New("Write error") + } + return len(p), nil +} +func (ew *gzErrWriter) Close() error { + if ew.failClose { + return errors.New("Close error") + } + return nil +} + +func TestCompressResponseWriteError(t *testing.T) { + _, err := compressResponse(nil, &gzErrWriter{failWrite: true}) + if err == nil { + t.Error("compressResponse() didn't return any error") + } +} + +func TestCompressResponseCloseError(t *testing.T) { + _, err := compressResponse(nil, &gzErrWriter{failClose: true}) + if err == nil { + t.Error("compressResponse() didn't return any error") + } +} + +type gzErrReader struct { + failAfter int + reads int +} + +func (er *gzErrReader) Read(p []byte) (n int, err error) { + if er.reads >= er.failAfter { + return 0, errors.New("Read error") + } + er.reads++ + + b, err := compressResponse([]byte("abcd"), nil) + if err != nil { + return 0, err + } + + return bytes.NewReader(b).Read(p) +} + +func TestDecompressResponseResetError(t *testing.T) { + _, err := decompressCachedResponse(&gzErrReader{failAfter: 0}) + if err == nil { + t.Error("decompressCachedResponse() didn't return any error") + return + } + if err.Error() != "failed to created new compression reader: Read error" { + t.Errorf("decompressCachedResponse() returned wrong error: %s", err) + } +} + +func TestDecompressResponseReadError(t *testing.T) { + _, err := decompressCachedResponse(&gzErrReader{failAfter: 1}) + if err == nil { + t.Error("decompressCachedResponse() didn't return any error") + return + } + if err.Error() != "failed to decompress data: Read error" { + t.Errorf("decompressCachedResponse() returned wrong error: %s", err) + } +} diff --git a/internal/alertmanager/tls.go b/internal/alertmanager/tls.go index 10e53256b..44ba11132 100644 --- a/internal/alertmanager/tls.go +++ b/internal/alertmanager/tls.go @@ -3,8 +3,8 @@ package alertmanager import ( "crypto/tls" "crypto/x509" - "io/ioutil" "net/http" + "os" "github.com/rs/zerolog/log" ) @@ -13,7 +13,7 @@ func configureTLSRootCAs(tlsConfig *tls.Config, caPath string) error { log.Debug(). Str("path", caPath). Msg("Loading TLS CA cert") - caCert, err := ioutil.ReadFile(caPath) + caCert, err := os.ReadFile(caPath) if err != nil { return err } diff --git a/internal/config/acl.go b/internal/config/acl.go index 0bf2710fd..9f9215ad8 100644 --- a/internal/config/acl.go +++ b/internal/config/acl.go @@ -2,7 +2,7 @@ package config import ( "fmt" - "io/ioutil" + "os" yaml "gopkg.in/yaml.v2" ) @@ -47,7 +47,7 @@ type silencesACLSchema struct { func ReadSilenceACLConfig(path string) (*silencesACLSchema, error) { cfg := silencesACLSchema{} - f, err := ioutil.ReadFile(path) + f, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("failed to load silence ACL configuration file %q: %v", path, err) } diff --git a/internal/config/config.go b/internal/config/config.go index fb075279b..62665ee83 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "fmt" - "io/ioutil" "os" "strings" "time" @@ -130,7 +129,7 @@ func SetupFlags(f *pflag.FlagSet) { } func validateConfigFile(path string) error { - f, err := ioutil.ReadFile(path) + f, err := os.ReadFile(path) if err != nil { return err } diff --git a/internal/mock/mock.go b/internal/mock/mock.go index a65e13e20..75e8d0436 100644 --- a/internal/mock/mock.go +++ b/internal/mock/mock.go @@ -1,7 +1,6 @@ package mock import ( - "io/ioutil" "os" "path" "path/filepath" @@ -20,7 +19,7 @@ func GetAbsoluteMockPath(filename string, version string) string { // GetMockResponder returns a httpmock.Responder for given file/version func GetMockResponder(url string, version string, filename string) httpmock.Responder { fullPath := GetAbsoluteMockPath(filename, version) - mockJSON, _ := ioutil.ReadFile(fullPath) + mockJSON, _ := os.ReadFile(fullPath) return httpmock.NewBytesResponder(200, mockJSON) } @@ -34,7 +33,7 @@ func ListAllMocks() []string { _, f, _, _ := runtime.Caller(0) cwd := filepath.Dir(f) - dirents, _ := ioutil.ReadDir(cwd) + dirents, _ := os.ReadDir(cwd) dirs := []string{} for _, dirent := range dirents { if dirent.IsDir() {