...

Source file src/cloud.google.com/go/logging/logadmin/logadmin_test.go

Documentation: cloud.google.com/go/logging/logadmin

     1  // Copyright 2016 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // TODO(jba): test that OnError is getting called appropriately.
    16  
    17  package logadmin
    18  
    19  import (
    20  	"context"
    21  	"flag"
    22  	"log"
    23  	"net/http"
    24  	"net/url"
    25  	"os"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"cloud.google.com/go/internal/testutil"
    31  	"cloud.google.com/go/logging"
    32  	logpb "cloud.google.com/go/logging/apiv2/loggingpb"
    33  	ltesting "cloud.google.com/go/logging/internal/testing"
    34  	"github.com/google/go-cmp/cmp/cmpopts"
    35  	"google.golang.org/api/option"
    36  	mrpb "google.golang.org/genproto/googleapis/api/monitoredres"
    37  	audit "google.golang.org/genproto/googleapis/cloud/audit"
    38  	logtypepb "google.golang.org/genproto/googleapis/logging/type"
    39  	"google.golang.org/grpc"
    40  	"google.golang.org/protobuf/types/known/anypb"
    41  	durpb "google.golang.org/protobuf/types/known/durationpb"
    42  	structpb "google.golang.org/protobuf/types/known/structpb"
    43  	"google.golang.org/protobuf/types/known/timestamppb"
    44  )
    45  
    46  var (
    47  	client        *Client
    48  	testProjectID string
    49  )
    50  
    51  var (
    52  	// If true, this test is using the production service, not a fake.
    53  	integrationTest bool
    54  
    55  	newClient func(ctx context.Context, projectID string) *Client
    56  )
    57  
    58  func TestMain(m *testing.M) {
    59  	flag.Parse() // needed for testing.Short()
    60  	ctx := context.Background()
    61  	testProjectID = testutil.ProjID()
    62  	if testProjectID == "" || testing.Short() {
    63  		integrationTest = false
    64  		if testProjectID != "" {
    65  			log.Print("Integration tests skipped in short mode (using fake instead)")
    66  		}
    67  		testProjectID = "PROJECT_ID"
    68  		addr, err := ltesting.NewServer()
    69  		if err != nil {
    70  			log.Fatalf("creating fake server: %v", err)
    71  		}
    72  		newClient = func(ctx context.Context, projectID string) *Client {
    73  			conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithBlock())
    74  			if err != nil {
    75  				log.Fatalf("dialing %q: %v", addr, err)
    76  			}
    77  			c, err := NewClient(ctx, projectID, option.WithGRPCConn(conn))
    78  			if err != nil {
    79  				log.Fatalf("creating client for fake at %q: %v", addr, err)
    80  			}
    81  			return c
    82  		}
    83  	} else {
    84  		integrationTest = true
    85  		ts := testutil.TokenSource(ctx, logging.AdminScope)
    86  		if ts == nil {
    87  			log.Fatal("The project key must be set. See CONTRIBUTING.md for details")
    88  		}
    89  		log.Printf("running integration tests with project %s", testProjectID)
    90  		newClient = func(ctx context.Context, projectID string) *Client {
    91  			c, err := NewClient(ctx, projectID, option.WithTokenSource(ts),
    92  				option.WithGRPCDialOption(grpc.WithBlock()))
    93  			if err != nil {
    94  				log.Fatalf("creating prod client: %v", err)
    95  			}
    96  			return c
    97  		}
    98  	}
    99  	client = newClient(ctx, testProjectID)
   100  	initMetrics(ctx)
   101  	cleanup := initSinks(ctx)
   102  	exit := m.Run()
   103  	cleanup()
   104  	client.Close()
   105  	os.Exit(exit)
   106  }
   107  
   108  // EntryIterator and DeleteLog are tested in the logging package.
   109  
   110  func TestClientClose(t *testing.T) {
   111  	c := newClient(context.Background(), testProjectID)
   112  	if err := c.Close(); err != nil {
   113  		t.Errorf("want got %v, want nil", err)
   114  	}
   115  }
   116  
   117  func TestFromLogEntry(t *testing.T) {
   118  	now := time.Now()
   119  	res := &mrpb.MonitoredResource{Type: "global"}
   120  	ts := timestamppb.New(now)
   121  	logEntry := logpb.LogEntry{
   122  		LogName:   "projects/PROJECT_ID/logs/LOG_ID",
   123  		Resource:  res,
   124  		Payload:   &logpb.LogEntry_TextPayload{TextPayload: "hello"},
   125  		Timestamp: ts,
   126  		Severity:  logtypepb.LogSeverity_INFO,
   127  		InsertId:  "123",
   128  		HttpRequest: &logtypepb.HttpRequest{
   129  			RequestMethod:                  "GET",
   130  			RequestUrl:                     "http:://example.com/path?q=1",
   131  			RequestSize:                    100,
   132  			Status:                         200,
   133  			ResponseSize:                   25,
   134  			Latency:                        &durpb.Duration{Seconds: 100},
   135  			UserAgent:                      "user-agent",
   136  			RemoteIp:                       "127.0.0.1",
   137  			ServerIp:                       "127.0.0.1",
   138  			Referer:                        "referer",
   139  			CacheLookup:                    true,
   140  			CacheHit:                       true,
   141  			CacheValidatedWithOriginServer: true,
   142  			CacheFillBytes:                 2048,
   143  		},
   144  		Labels: map[string]string{
   145  			"a": "1",
   146  			"b": "two",
   147  			"c": "true",
   148  		},
   149  		SourceLocation: &logpb.LogEntrySourceLocation{
   150  			File:     "some_file.go",
   151  			Line:     1,
   152  			Function: "someFunction",
   153  		},
   154  	}
   155  	u, err := url.Parse("http:://example.com/path?q=1")
   156  	if err != nil {
   157  		t.Fatal(err)
   158  	}
   159  	want := &logging.Entry{
   160  		LogName:   "projects/PROJECT_ID/logs/LOG_ID",
   161  		Resource:  res,
   162  		Timestamp: now.In(time.UTC),
   163  		Severity:  logging.Info,
   164  		Payload:   "hello",
   165  		Labels: map[string]string{
   166  			"a": "1",
   167  			"b": "two",
   168  			"c": "true",
   169  		},
   170  		InsertID: "123",
   171  		HTTPRequest: &logging.HTTPRequest{
   172  			Request: &http.Request{
   173  				Method: "GET",
   174  				URL:    u,
   175  				Header: map[string][]string{
   176  					"User-Agent": {"user-agent"},
   177  					"Referer":    {"referer"},
   178  				},
   179  			},
   180  			RequestSize:                    100,
   181  			Status:                         200,
   182  			ResponseSize:                   25,
   183  			Latency:                        100 * time.Second,
   184  			LocalIP:                        "127.0.0.1",
   185  			RemoteIP:                       "127.0.0.1",
   186  			CacheLookup:                    true,
   187  			CacheHit:                       true,
   188  			CacheValidatedWithOriginServer: true,
   189  			CacheFillBytes:                 2048,
   190  		},
   191  		SourceLocation: &logpb.LogEntrySourceLocation{
   192  			File:     "some_file.go",
   193  			Line:     1,
   194  			Function: "someFunction",
   195  		},
   196  	}
   197  	got, err := fromLogEntry(&logEntry)
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  	if diff := testutil.Diff(got, want, cmpopts.IgnoreUnexported(http.Request{})); diff != "" {
   202  		t.Errorf("FullEntry:\n%s", diff)
   203  	}
   204  
   205  	// Proto payload.
   206  	alog := &audit.AuditLog{
   207  		ServiceName:  "svc",
   208  		MethodName:   "method",
   209  		ResourceName: "shelves/S/books/B",
   210  	}
   211  	any, err := anypb.New(alog)
   212  	if err != nil {
   213  		t.Fatal(err)
   214  	}
   215  	logEntry = logpb.LogEntry{
   216  		LogName:   "projects/PROJECT_ID/logs/LOG_ID",
   217  		Resource:  res,
   218  		Timestamp: ts,
   219  		Payload:   &logpb.LogEntry_ProtoPayload{ProtoPayload: any},
   220  	}
   221  	got, err = fromLogEntry(&logEntry)
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  	if !ltesting.PayloadEqual(got.Payload, alog) {
   226  		t.Errorf("got %+v, want %+v", got.Payload, alog)
   227  	}
   228  
   229  	// JSON payload.
   230  	jstruct := &structpb.Struct{Fields: map[string]*structpb.Value{
   231  		"f": {Kind: &structpb.Value_NumberValue{NumberValue: 3.1}},
   232  	}}
   233  	logEntry = logpb.LogEntry{
   234  		LogName:   "projects/PROJECT_ID/logs/LOG_ID",
   235  		Resource:  res,
   236  		Timestamp: ts,
   237  		Payload:   &logpb.LogEntry_JsonPayload{JsonPayload: jstruct},
   238  	}
   239  	got, err = fromLogEntry(&logEntry)
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  	if !ltesting.PayloadEqual(got.Payload, jstruct) {
   244  		t.Errorf("got %+v, want %+v", got.Payload, jstruct)
   245  	}
   246  
   247  	// No payload.
   248  	logEntry = logpb.LogEntry{
   249  		LogName:   "projects/PROJECT_ID/logs/LOG_ID",
   250  		Resource:  res,
   251  		Timestamp: ts,
   252  	}
   253  	got, err = fromLogEntry(&logEntry)
   254  	if err != nil {
   255  		t.Fatal(err)
   256  	}
   257  	if !ltesting.PayloadEqual(got.Payload, nil) {
   258  		t.Errorf("got %+v, want %+v", got.Payload, nil)
   259  	}
   260  }
   261  
   262  func TestListLogEntriesRequestDefaults(t *testing.T) {
   263  	const timeFilterPrefix = "timestamp >= "
   264  
   265  	got := listLogEntriesRequest("projects/PROJECT_ID", nil)
   266  
   267  	// parse time from filter
   268  	if len(got.Filter) < len(timeFilterPrefix) {
   269  		t.Errorf("got %v; want len(%v) start with '%v'", got, got.Filter, timeFilterPrefix)
   270  	}
   271  	filterTime, err := time.Parse(time.RFC3339, strings.Trim(got.Filter[len(timeFilterPrefix):], "\""))
   272  	if err != nil {
   273  		t.Errorf("got %v; want %v in RFC3339", err, got.Filter)
   274  	}
   275  	timeDiff := time.Now().UTC().Sub(filterTime)
   276  
   277  	// Default is client's project ID, 24 hour lookback, and no orderBy.
   278  	if !testutil.Equal(got.ResourceNames, []string{"projects/PROJECT_ID"}) || got.OrderBy != "" || timeDiff.Hours() < 24 {
   279  		t.Errorf("got %v; want resource_names:\"projects/PROJECT_ID\" filter: %v - 24 hours order_by:\"\"", got, filterTime)
   280  	}
   281  }
   282  
   283  func TestListLogEntriesRequest(t *testing.T) {
   284  	for _, test := range []struct {
   285  		opts          []EntriesOption
   286  		resourceNames []string
   287  		filterPrefix  string
   288  		orderBy       string
   289  		pageSize      int32
   290  	}{
   291  		// Timestamp default does not override user's filter
   292  		{
   293  			// default resource name and timestamp filter
   294  			opts: []EntriesOption{
   295  				NewestFirst(),
   296  				Filter(`timestamp > "2020-10-30T15:39:09Z"`),
   297  			},
   298  			resourceNames: []string{"projects/PROJECT_ID"},
   299  			filterPrefix:  `timestamp > "2020-10-30T15:39:09Z"`,
   300  			orderBy:       "timestamp desc",
   301  		},
   302  		{
   303  			// default resource name and user's filter
   304  			opts: []EntriesOption{
   305  				NewestFirst(),
   306  				Filter("f"),
   307  			},
   308  			resourceNames: []string{"projects/PROJECT_ID"},
   309  			filterPrefix:  "f AND timestamp >= \"",
   310  			orderBy:       "timestamp desc",
   311  		},
   312  		{
   313  			// user's project id and default timestamp filter
   314  			opts: []EntriesOption{
   315  				ProjectIDs([]string{"foo"}),
   316  			},
   317  			resourceNames: []string{"projects/foo"},
   318  			filterPrefix:  "timestamp >= \"",
   319  			orderBy:       "",
   320  		},
   321  		{
   322  			// user's resource name and default timestamp filter
   323  			opts: []EntriesOption{
   324  				ResourceNames([]string{"folders/F", "organizations/O"}),
   325  			},
   326  			resourceNames: []string{"folders/F", "organizations/O"},
   327  			filterPrefix:  "timestamp >= \"",
   328  			orderBy:       "",
   329  		},
   330  		{
   331  			// user's project id and user's options
   332  			opts: []EntriesOption{
   333  				NewestFirst(),
   334  				Filter("f"),
   335  				ProjectIDs([]string{"foo"}),
   336  			},
   337  			resourceNames: []string{"projects/foo"},
   338  			filterPrefix:  "f AND timestamp >= \"",
   339  			orderBy:       "timestamp desc",
   340  		},
   341  		{
   342  			// user's project id with multiple filter options
   343  			opts: []EntriesOption{
   344  				NewestFirst(),
   345  				Filter("no"),
   346  				ProjectIDs([]string{"foo"}),
   347  				Filter("f"),
   348  			},
   349  			resourceNames: []string{"projects/foo"},
   350  			filterPrefix:  "f AND timestamp >= \"",
   351  			orderBy:       "timestamp desc",
   352  		},
   353  		{
   354  			// user's project id and custom page size
   355  			opts: []EntriesOption{
   356  				ProjectIDs([]string{"foo"}),
   357  				PageSize(100),
   358  			},
   359  			resourceNames: []string{"projects/foo"},
   360  			filterPrefix:  "timestamp >= \"",
   361  			pageSize:      100,
   362  		},
   363  	} {
   364  		got := listLogEntriesRequest("projects/PROJECT_ID", test.opts)
   365  		want := &logpb.ListLogEntriesRequest{
   366  			ResourceNames: test.resourceNames,
   367  			Filter:        test.filterPrefix,
   368  			OrderBy:       test.orderBy,
   369  			PageSize:      test.pageSize,
   370  		}
   371  		if !testutil.Equal(got.ResourceNames, want.ResourceNames) || !strings.HasPrefix(got.Filter, want.Filter) || got.OrderBy != want.OrderBy || got.PageSize != want.PageSize {
   372  			t.Errorf("got: %v; want %v (mind wanted Filter is prefix)", got, want)
   373  		}
   374  	}
   375  }
   376  

View as plain text