blob: d273184de002ca4b51cdf9684df9fd9b1c747ae2 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package logs
import (
"bytes"
"context"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"os"
"time"
"chromiumos/tast/common/testexec"
"chromiumos/tast/errors"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: LogRotate,
Desc: "Tests to run log_rotator",
Contacts: []string{"yoshiki@chromium.org"},
Attr: []string{"group:mainline", "informational"},
SoftwareDeps: []string{"chrome"},
})
}
func LogRotate(ctx context.Context, s *testing.State) {
const (
logRotatorExecutable = "/usr/sbin/log_rotator"
// Days we would keep the old file.
// Log file is rotated daily, so that "7" means 7 log files are kept in maximum.
// This value must match with "DAYS_TO_PRESERVE_LOGS" in //croslog/log_rotate/log_rotate.cc
daysToPreserveLogs = 7
)
// Prepares log files to rotate.
err := createLogFiles(s, daysToPreserveLogs)
if err != nil {
s.Error("Creating log files failed: ", err)
}
// Removes the old log file which should not exist.
tooOldPath := getLogFilePathWithIndex(daysToPreserveLogs + 1)
if _, err := os.Stat(tooOldPath); !os.IsNotExist(err) {
err = os.Remove(tooOldPath)
if err != nil {
s.Errorf("Removing file failed %q: %v", tooOldPath, err)
}
}
// Calculates the hashes for the files to check the identity of files
var hashes [daysToPreserveLogs][]byte
for i := 0; i < daysToPreserveLogs; i++ {
path := getLogFilePathWithIndex(i)
hashes[i], _ = calculateHashOfFile(path)
}
// Runs log_rotater command.
s.Log("Running log_rotator")
out, err := testexec.CommandContext(ctx, logRotatorExecutable).Output()
if err != nil {
s.Errorf("Executing log_rotator failed with output %q: %v", out, err)
}
// Ensures the base log file is gone by the rotation.
basePath := getLogFilePathWithIndex(0)
if _, err := os.Stat(basePath); !os.IsNotExist(err) {
s.Errorf("The log file should not exist %q: %v", basePath, err)
}
// Compares the hashes to check if the rotation is done correctly.
for i := 1; i <= daysToPreserveLogs; i++ {
newPath := getLogFilePathWithIndex(i)
newHash, err := calculateHashOfFile(newPath)
if err != nil {
s.Errorf("Rotation failed. The calculating hash of the log file %q failed: %v", newPath, err)
}
if !bytes.Equal(hashes[i-1], newHash) {
s.Errorf("Rotation failed. The hash doesn't match (new path: %q)", newPath)
}
// Cleans up the log file.
err = os.Remove(newPath)
if err != nil {
s.Errorf("Removing file failed %q: %v", newPath, err)
}
}
// Ensures the too-old log is not created, since it's not within |daysToPreserveLogs|.
if _, err := os.Stat(tooOldPath); !os.IsNotExist(err) {
s.Errorf("The log file should not exist %q: %v", tooOldPath, err)
}
}
func calculateHashOfFile(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return []byte{}, errors.Wrap(err, "failed to open log files")
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return []byte{}, errors.Wrap(err, "failed to calculate the hash of the log file")
}
return h.Sum(nil), nil
}
func createLogsIfNotExist(path string, s *testing.State) error {
if _, err := os.Stat(path); !os.IsNotExist(err) {
return nil
}
s.Logf("Creating a log file %q", path)
contents := "THIS FILE IS FOR TESTING " + time.Now().Format("2006-01-02 15:04:05.000000000")
err := ioutil.WriteFile(path, []byte(contents), 0644)
if err != nil {
return errors.Wrapf(err, "failed to create log files %q", path)
}
return nil
}
// createLogFiles prepares (daysToPreserveLogs + 1) files.
func createLogFiles(s *testing.State, daysToPreserveLogs int) error {
for i := 0; i <= daysToPreserveLogs; i++ {
path := getLogFilePathWithIndex(i)
err := createLogsIfNotExist(path, s)
if err != nil {
return errors.Wrapf(err, "failed to create a log file %q", path)
}
}
return nil
}
// getLogFilePathWithIndex returns log file path with the specified index.
func getLogFilePathWithIndex(index int) string {
if index == 0 {
return "/var/log/temporary_log_file_for_testing.log"
}
return fmt.Sprintf("/var/log/temporary_log_file_for_testing.%d.log", index)
}