...

Source file src/github.com/coreos/go-systemd/v22/sdjournal/journal_test.go

Documentation: github.com/coreos/go-systemd/v22/sdjournal

     1  // Copyright 2015 RedHat, Inc.
     2  // Copyright 2015 CoreOS, Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package sdjournal
    17  
    18  import (
    19  	"bytes"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"math/rand"
    25  	"os"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/coreos/go-systemd/v22/journal"
    31  )
    32  
    33  func TestJournalFollow(t *testing.T) {
    34  	r, err := NewJournalReader(JournalReaderConfig{
    35  		Since: time.Duration(-15) * time.Second,
    36  		Matches: []Match{
    37  			{
    38  				Field: SD_JOURNAL_FIELD_SYSTEMD_UNIT,
    39  				Value: "NetworkManager.service",
    40  			},
    41  		},
    42  	})
    43  
    44  	if err != nil {
    45  		t.Fatalf("Error opening journal: %s", err)
    46  	}
    47  
    48  	if r == nil {
    49  		t.Fatal("Got a nil reader")
    50  	}
    51  
    52  	defer r.Close()
    53  
    54  	// start writing some test entries
    55  	done := make(chan struct{}, 1)
    56  	errCh := make(chan error, 1)
    57  	defer close(done)
    58  	go func() {
    59  		for {
    60  			select {
    61  			case <-done:
    62  				return
    63  			default:
    64  				if perr := journal.Print(journal.PriInfo, "test message %s", time.Now()); err != nil {
    65  					errCh <- perr
    66  					return
    67  				}
    68  
    69  				time.Sleep(time.Second)
    70  			}
    71  		}
    72  	}()
    73  
    74  	// and follow the reader synchronously
    75  	timeout := time.Duration(5) * time.Second
    76  	if err = r.Follow(time.After(timeout), os.Stdout); err != ErrExpired {
    77  		t.Fatalf("Error during follow: %s", err)
    78  	}
    79  
    80  	select {
    81  	case err := <-errCh:
    82  		t.Fatalf("Error writing to journal: %s", err)
    83  	default:
    84  	}
    85  }
    86  
    87  func TestJournalWait(t *testing.T) {
    88  	id := time.Now().String()
    89  	j, err := NewJournal()
    90  	if err != nil {
    91  		t.Fatalf("Error opening journal: %s", err)
    92  	}
    93  	if err := j.AddMatch("TEST=TestJournalWait " + id); err != nil {
    94  		t.Fatalf("Error adding match: %s", err)
    95  	}
    96  	if err := j.SeekTail(); err != nil {
    97  		t.Fatalf("Error seeking to tail: %s", err)
    98  	}
    99  	if _, err := j.Previous(); err != nil {
   100  		t.Fatalf("Error retrieving previous entry: %s", err)
   101  	}
   102  
   103  	var t1, t2 time.Time
   104  	for ret := -1; ret != SD_JOURNAL_NOP; {
   105  		// Wait() might return for reasons other than timeout.
   106  		// For example the first call initializes stuff and returns immediately.
   107  		t1 = time.Now()
   108  		ret = j.Wait(time.Millisecond * 300)
   109  		t2 = time.Now()
   110  	}
   111  	duration := t2.Sub(t1)
   112  
   113  	if duration > time.Millisecond*325 || duration < time.Millisecond*300 {
   114  		t.Errorf("Wait did not wait 300ms. Actually waited %s", duration.String())
   115  	}
   116  
   117  	journal.Send("test message", journal.PriInfo, map[string]string{"TEST": "TestJournalWait " + id})
   118  	for ret := -1; ret != SD_JOURNAL_APPEND; {
   119  		t1 = time.Now()
   120  		ret = j.Wait(time.Millisecond * 300)
   121  		t2 = time.Now()
   122  	}
   123  	duration = t2.Sub(t1)
   124  
   125  	if duration >= time.Millisecond*300 {
   126  		t.Errorf("Wait took longer than 300ms. Actual duration %s", duration.String())
   127  	}
   128  }
   129  
   130  func TestJournalGetUsage(t *testing.T) {
   131  	j, err := NewJournal()
   132  
   133  	if err != nil {
   134  		t.Fatalf("Error opening journal: %s", err)
   135  	}
   136  
   137  	if j == nil {
   138  		t.Fatal("Got a nil journal")
   139  	}
   140  
   141  	defer j.Close()
   142  
   143  	_, err = j.GetUsage()
   144  
   145  	if err != nil {
   146  		t.Fatalf("Error getting journal size: %s", err)
   147  	}
   148  }
   149  
   150  func TestJournalCursorGetSeekAndTest(t *testing.T) {
   151  	j, err := NewJournal()
   152  	if err != nil {
   153  		t.Fatalf("Error opening journal: %s", err)
   154  	}
   155  
   156  	if j == nil {
   157  		t.Fatal("Got a nil journal")
   158  	}
   159  
   160  	defer j.Close()
   161  
   162  	err = journal.Print(journal.PriInfo, "test message for cursor %s", time.Now())
   163  	if err != nil {
   164  		t.Fatalf("Error writing to journal: %s", err)
   165  	}
   166  
   167  	if err = waitAndNext(j); err != nil {
   168  		t.Fatalf(err.Error())
   169  	}
   170  
   171  	c, err := j.GetCursor()
   172  	if err != nil {
   173  		t.Fatalf("Error getting cursor from journal: %s", err)
   174  	}
   175  
   176  	err = j.SeekCursor(c)
   177  	if err != nil {
   178  		t.Fatalf("Error seeking cursor to journal: %s", err)
   179  	}
   180  
   181  	if err = waitAndNext(j); err != nil {
   182  		t.Fatalf(err.Error())
   183  	}
   184  
   185  	err = j.TestCursor(c)
   186  	if err != nil {
   187  		t.Fatalf("Error testing cursor to journal: %s", err)
   188  	}
   189  
   190  	err = journal.Print(journal.PriInfo, "second message %s", time.Now())
   191  	if err != nil {
   192  		t.Fatalf("Error writing to journal: %s", err)
   193  	}
   194  
   195  	if err = waitAndNext(j); err != nil {
   196  		t.Fatalf(err.Error())
   197  	}
   198  
   199  	err = j.TestCursor(c)
   200  	if err != ErrNoTestCursor {
   201  		t.Fatalf("Error, TestCursor should fail because current cursor has moved from the previous obtained cursor")
   202  	}
   203  }
   204  
   205  func TestNewJournalFromDir(t *testing.T) {
   206  	// test for error handling
   207  	dir := "/ClearlyNonExistingPath/"
   208  	j, err := NewJournalFromDir(dir)
   209  	if err == nil {
   210  		defer j.Close()
   211  		t.Fatalf("Error expected when opening dummy path (%s)", dir)
   212  	}
   213  	// test for main code path
   214  	dir, err = ioutil.TempDir("", "go-systemd-test")
   215  	if err != nil {
   216  		t.Fatalf("Error creating tempdir: %s", err)
   217  	}
   218  	defer os.RemoveAll(dir)
   219  	j, err = NewJournalFromDir(dir)
   220  	if err != nil {
   221  		t.Fatalf("Error opening journal: %s", err)
   222  	}
   223  	if j == nil {
   224  		t.Fatal("Got a nil journal")
   225  	}
   226  	j.Close()
   227  }
   228  
   229  func setupJournalRoundtrip() (*Journal, map[string]string, error) {
   230  	j, err := NewJournal()
   231  	if err != nil {
   232  		return nil, nil, fmt.Errorf("Error opening journal: %s", err)
   233  	}
   234  
   235  	if j == nil {
   236  		return nil, nil, fmt.Errorf("Got a nil journal")
   237  	}
   238  
   239  	j.FlushMatches()
   240  
   241  	matchField := "TESTJOURNALENTRY"
   242  	matchValue := fmt.Sprintf("%d", time.Now().UnixNano())
   243  	m := Match{Field: matchField, Value: matchValue}
   244  	err = j.AddMatch(m.String())
   245  	if err != nil {
   246  		return nil, nil, fmt.Errorf("Error adding matches to journal: %s", err)
   247  	}
   248  
   249  	msg := fmt.Sprintf("test journal get entry message %s", time.Now())
   250  	data := map[string]string{matchField: matchValue}
   251  	err = journal.Send(msg, journal.PriInfo, data)
   252  	if err != nil {
   253  		return nil, nil, fmt.Errorf("Error writing to journal: %s", err)
   254  	}
   255  
   256  	time.Sleep(time.Duration(1) * time.Second)
   257  
   258  	n, err := j.Next()
   259  	if err != nil {
   260  		return nil, nil, fmt.Errorf("Error reading from journal: %s", err)
   261  	}
   262  
   263  	if n == 0 {
   264  		return nil, nil, fmt.Errorf("Error reading from journal: %s", io.EOF)
   265  	}
   266  
   267  	data["MESSAGE"] = msg
   268  
   269  	return j, data, nil
   270  }
   271  
   272  func TestJournalGetData(t *testing.T) {
   273  	j, wantEntry, err := setupJournalRoundtrip()
   274  	if err != nil {
   275  		t.Fatal(err.Error())
   276  	}
   277  
   278  	defer j.Close()
   279  
   280  	for k, v := range wantEntry {
   281  		data := fmt.Sprintf("%s=%s", k, v)
   282  
   283  		dataStr, err := j.GetData(k)
   284  		if err != nil {
   285  			t.Fatalf("GetData() error: %v", err)
   286  		}
   287  
   288  		if dataStr != data {
   289  			t.Fatalf("Invalid data for \"%s\": got %s, want %s", k, dataStr, data)
   290  		}
   291  
   292  		dataBytes, err := j.GetDataBytes(k)
   293  		if err != nil {
   294  			t.Fatalf("GetDataBytes() error: %v", err)
   295  		}
   296  
   297  		if string(dataBytes) != data {
   298  			t.Fatalf("Invalid data bytes for \"%s\": got %s, want %s", k, string(dataBytes), data)
   299  		}
   300  
   301  		valStr, err := j.GetDataValue(k)
   302  		if err != nil {
   303  			t.Fatalf("GetDataValue() error: %v", err)
   304  		}
   305  
   306  		if valStr != v {
   307  			t.Fatalf("Invalid data value for \"%s\": got %s, want %s", k, valStr, v)
   308  		}
   309  
   310  		valBytes, err := j.GetDataValueBytes(k)
   311  		if err != nil {
   312  			t.Fatalf("GetDataValueBytes() error: %v", err)
   313  		}
   314  
   315  		if string(valBytes) != v {
   316  			t.Fatalf("Invalid data value bytes for \"%s\": got %s, want %s", k, string(valBytes), v)
   317  		}
   318  	}
   319  }
   320  
   321  func TestJournalGetEntry(t *testing.T) {
   322  	j, wantEntry, err := setupJournalRoundtrip()
   323  	if err != nil {
   324  		t.Fatal(err.Error())
   325  	}
   326  
   327  	defer j.Close()
   328  
   329  	entry, err := j.GetEntry()
   330  	if err != nil {
   331  		t.Fatalf("Error getting the entry to journal: %s", err)
   332  	}
   333  
   334  	for k, wantV := range wantEntry {
   335  		gotV := entry.Fields[k]
   336  		if gotV != wantV {
   337  			t.Fatalf("Bad result for entry.Fields[\"%s\"]: got %s, want %s", k, gotV, wantV)
   338  		}
   339  	}
   340  }
   341  
   342  // Check for incorrect read into small buffers,
   343  // see https://github.com/coreos/go-systemd/issues/172
   344  func TestJournalReaderSmallReadBuffer(t *testing.T) {
   345  	// Write a long entry ...
   346  	delim := "%%%%%%"
   347  	longEntry := strings.Repeat("a", 256)
   348  	matchField := "TESTJOURNALREADERSMALLBUF"
   349  	matchValue := fmt.Sprintf("%d", time.Now().UnixNano())
   350  	r, err := NewJournalReader(JournalReaderConfig{
   351  		Since: time.Duration(-15) * time.Second,
   352  		Matches: []Match{
   353  			{
   354  				Field: matchField,
   355  				Value: matchValue,
   356  			},
   357  		},
   358  	})
   359  	if err != nil {
   360  		t.Fatalf("Error opening journal: %s", err)
   361  	}
   362  	if r == nil {
   363  		t.Fatal("Got a nil reader")
   364  	}
   365  	defer r.Close()
   366  
   367  	want := fmt.Sprintf("%slongentry %s%s", delim, longEntry, delim)
   368  	err = journal.Send(want, journal.PriInfo, map[string]string{matchField: matchValue})
   369  	if err != nil {
   370  		t.Fatal("Error writing to journal", err)
   371  	}
   372  	time.Sleep(time.Second)
   373  
   374  	// ... and try to read it back piece by piece via a small buffer
   375  	finalBuff := new(bytes.Buffer)
   376  	var e error
   377  	for c := -1; c != 0 && e == nil; {
   378  		smallBuf := make([]byte, 5)
   379  		c, e = r.Read(smallBuf)
   380  		if c > len(smallBuf) {
   381  			t.Fatalf("Got unexpected read length: %d vs %d", c, len(smallBuf))
   382  		}
   383  		_, _ = finalBuff.Write(smallBuf)
   384  	}
   385  	b := finalBuff.String()
   386  	got := strings.Split(b, delim)
   387  	if len(got) != 3 {
   388  		t.Fatalf("Got unexpected entry %s", b)
   389  	}
   390  	if got[1] != strings.Trim(want, delim) {
   391  		t.Fatalf("Got unexpected message %s", got[1])
   392  	}
   393  }
   394  
   395  func TestJournalGetUniqueValues(t *testing.T) {
   396  	j, err := NewJournal()
   397  	if err != nil {
   398  		t.Fatal(err)
   399  	}
   400  
   401  	defer j.Close()
   402  
   403  	uniqueString := generateRandomField(20)
   404  	testEntries := []string{"A", "B", "C", "D"}
   405  	for _, v := range testEntries {
   406  		err = journal.Send("TEST: "+uniqueString, journal.PriInfo, map[string]string{uniqueString: v})
   407  		if err != nil {
   408  			t.Fatal(err)
   409  		}
   410  	}
   411  
   412  	// TODO: add proper `waitOnMatch` function which should wait for journal entry with filter to commit.
   413  	time.Sleep(time.Millisecond * 500)
   414  
   415  	values, err := j.GetUniqueValues(uniqueString)
   416  	if err != nil {
   417  		t.Fatal(err)
   418  	}
   419  
   420  	if len(values) != len(testEntries) {
   421  		t.Fatalf("Expect %d entries. Got %d", len(testEntries), len(values))
   422  	}
   423  
   424  	if !contains(values, "A") || !contains(values, "B") || !contains(values, "C") || !contains(values, "D") {
   425  		t.Fatalf("Expect 4 values for %s field: A,B,C,D. Got %s", uniqueString, values)
   426  	}
   427  }
   428  
   429  func TestJournalGetCatalog(t *testing.T) {
   430  	want := []string{
   431  		"Subject: ",
   432  		"Defined-By: systemd",
   433  		"Support: ",
   434  	}
   435  	j, err := NewJournal()
   436  	if err != nil {
   437  		t.Fatalf("Error opening journal: %s", err)
   438  	}
   439  
   440  	if j == nil {
   441  		t.Fatal("Got a nil journal")
   442  	}
   443  
   444  	defer j.Close()
   445  
   446  	if err = j.SeekHead(); err != nil {
   447  		t.Fatalf("Seek to head failed: %s", err)
   448  	}
   449  
   450  	matchField := SD_JOURNAL_FIELD_SYSTEMD_UNIT
   451  	m := Match{Field: matchField, Value: "systemd-journald.service"}
   452  	if err = j.AddMatch(m.String()); err != nil {
   453  		t.Fatalf("Error adding matches to journal: %s", err)
   454  	}
   455  
   456  	if err = waitAndNext(j); err != nil {
   457  		t.Fatalf(err.Error())
   458  	}
   459  
   460  	catalog, err := j.GetCatalog()
   461  
   462  	if err != nil {
   463  		t.Fatalf("Failed to retrieve catalog entry: %s", err)
   464  	}
   465  
   466  	for _, w := range want {
   467  		if !strings.Contains(catalog, w) {
   468  			t.Fatalf("Failed to find \"%s\" in \n%s", w, catalog)
   469  		}
   470  	}
   471  }
   472  
   473  func TestJournalGetBootID(t *testing.T) {
   474  	j, err := NewJournal()
   475  	if err != nil {
   476  		t.Fatal(err)
   477  	}
   478  
   479  	defer j.Close()
   480  
   481  	bootID, err := j.GetBootID()
   482  
   483  	if err != nil {
   484  		t.Fatalf("Failed to get bootID : %s", err)
   485  	}
   486  
   487  	if len(bootID) <= 0 {
   488  		t.Fatalf("Get bootID: %s is Null", bootID)
   489  	}
   490  
   491  	fmt.Printf("Test GetBootID: %s", bootID)
   492  }
   493  
   494  func contains(s []string, v string) bool {
   495  	for _, entry := range s {
   496  		if entry == v {
   497  			return true
   498  		}
   499  	}
   500  	return false
   501  }
   502  
   503  func generateRandomField(n int) string {
   504  	letters := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
   505  	s := make([]rune, n)
   506  	rand.Seed(time.Now().UnixNano())
   507  	for i := range s {
   508  		s[i] = letters[rand.Intn(len(letters))]
   509  	}
   510  	return string(s)
   511  }
   512  
   513  func waitAndNext(j *Journal) error {
   514  	r := j.Wait(time.Duration(1) * time.Second)
   515  	if r < 0 {
   516  		return errors.New("Error waiting to journal")
   517  	}
   518  
   519  	n, err := j.Next()
   520  	if err != nil {
   521  		return fmt.Errorf("Error reading to journal: %s", err)
   522  	}
   523  
   524  	if n == 0 {
   525  		return fmt.Errorf("Error reading to journal: %s", io.EOF)
   526  	}
   527  
   528  	return nil
   529  }
   530  

View as plain text