You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

281 line
8.1 KiB

  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. // TODO(jba): test that OnError is getting called appropriately.
  15. package logadmin
  16. import (
  17. "context"
  18. "flag"
  19. "log"
  20. "net/http"
  21. "net/url"
  22. "os"
  23. "testing"
  24. "time"
  25. "cloud.google.com/go/internal/testutil"
  26. "cloud.google.com/go/logging"
  27. ltesting "cloud.google.com/go/logging/internal/testing"
  28. "github.com/golang/protobuf/proto"
  29. "github.com/golang/protobuf/ptypes"
  30. durpb "github.com/golang/protobuf/ptypes/duration"
  31. structpb "github.com/golang/protobuf/ptypes/struct"
  32. "github.com/google/go-cmp/cmp/cmpopts"
  33. "google.golang.org/api/option"
  34. mrpb "google.golang.org/genproto/googleapis/api/monitoredres"
  35. audit "google.golang.org/genproto/googleapis/cloud/audit"
  36. logtypepb "google.golang.org/genproto/googleapis/logging/type"
  37. logpb "google.golang.org/genproto/googleapis/logging/v2"
  38. "google.golang.org/grpc"
  39. )
  40. var (
  41. client *Client
  42. testProjectID string
  43. )
  44. var (
  45. // If true, this test is using the production service, not a fake.
  46. integrationTest bool
  47. newClient func(ctx context.Context, projectID string) *Client
  48. )
  49. func TestMain(m *testing.M) {
  50. flag.Parse() // needed for testing.Short()
  51. ctx := context.Background()
  52. testProjectID = testutil.ProjID()
  53. if testProjectID == "" || testing.Short() {
  54. integrationTest = false
  55. if testProjectID != "" {
  56. log.Print("Integration tests skipped in short mode (using fake instead)")
  57. }
  58. testProjectID = "PROJECT_ID"
  59. addr, err := ltesting.NewServer()
  60. if err != nil {
  61. log.Fatalf("creating fake server: %v", err)
  62. }
  63. newClient = func(ctx context.Context, projectID string) *Client {
  64. conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithBlock())
  65. if err != nil {
  66. log.Fatalf("dialing %q: %v", addr, err)
  67. }
  68. c, err := NewClient(ctx, projectID, option.WithGRPCConn(conn))
  69. if err != nil {
  70. log.Fatalf("creating client for fake at %q: %v", addr, err)
  71. }
  72. return c
  73. }
  74. } else {
  75. // TODO(enocom): Delete this once we can get these tests to reliably pass.
  76. return
  77. integrationTest = true
  78. ts := testutil.TokenSource(ctx, logging.AdminScope)
  79. if ts == nil {
  80. log.Fatal("The project key must be set. See CONTRIBUTING.md for details")
  81. }
  82. log.Printf("running integration tests with project %s", testProjectID)
  83. newClient = func(ctx context.Context, projectID string) *Client {
  84. c, err := NewClient(ctx, projectID, option.WithTokenSource(ts),
  85. option.WithGRPCDialOption(grpc.WithBlock()))
  86. if err != nil {
  87. log.Fatalf("creating prod client: %v", err)
  88. }
  89. return c
  90. }
  91. }
  92. client = newClient(ctx, testProjectID)
  93. initMetrics(ctx)
  94. cleanup := initSinks(ctx)
  95. exit := m.Run()
  96. cleanup()
  97. client.Close()
  98. os.Exit(exit)
  99. }
  100. // EntryIterator and DeleteLog are tested in the logging package.
  101. func TestClientClose(t *testing.T) {
  102. c := newClient(context.Background(), testProjectID)
  103. if err := c.Close(); err != nil {
  104. t.Errorf("want got %v, want nil", err)
  105. }
  106. }
  107. func TestFromLogEntry(t *testing.T) {
  108. now := time.Now()
  109. res := &mrpb.MonitoredResource{Type: "global"}
  110. ts, err := ptypes.TimestampProto(now)
  111. if err != nil {
  112. t.Fatal(err)
  113. }
  114. logEntry := logpb.LogEntry{
  115. LogName: "projects/PROJECT_ID/logs/LOG_ID",
  116. Resource: res,
  117. Payload: &logpb.LogEntry_TextPayload{TextPayload: "hello"},
  118. Timestamp: ts,
  119. Severity: logtypepb.LogSeverity_INFO,
  120. InsertId: "123",
  121. HttpRequest: &logtypepb.HttpRequest{
  122. RequestMethod: "GET",
  123. RequestUrl: "http:://example.com/path?q=1",
  124. RequestSize: 100,
  125. Status: 200,
  126. ResponseSize: 25,
  127. Latency: &durpb.Duration{Seconds: 100},
  128. UserAgent: "user-agent",
  129. RemoteIp: "127.0.0.1",
  130. Referer: "referer",
  131. CacheHit: true,
  132. CacheValidatedWithOriginServer: true,
  133. },
  134. Labels: map[string]string{
  135. "a": "1",
  136. "b": "two",
  137. "c": "true",
  138. },
  139. SourceLocation: &logpb.LogEntrySourceLocation{
  140. File: "some_file.go",
  141. Line: 1,
  142. Function: "someFunction",
  143. },
  144. }
  145. u, err := url.Parse("http:://example.com/path?q=1")
  146. if err != nil {
  147. t.Fatal(err)
  148. }
  149. want := &logging.Entry{
  150. LogName: "projects/PROJECT_ID/logs/LOG_ID",
  151. Resource: res,
  152. Timestamp: now.In(time.UTC),
  153. Severity: logging.Info,
  154. Payload: "hello",
  155. Labels: map[string]string{
  156. "a": "1",
  157. "b": "two",
  158. "c": "true",
  159. },
  160. InsertID: "123",
  161. HTTPRequest: &logging.HTTPRequest{
  162. Request: &http.Request{
  163. Method: "GET",
  164. URL: u,
  165. Header: map[string][]string{
  166. "User-Agent": {"user-agent"},
  167. "Referer": {"referer"},
  168. },
  169. },
  170. RequestSize: 100,
  171. Status: 200,
  172. ResponseSize: 25,
  173. Latency: 100 * time.Second,
  174. RemoteIP: "127.0.0.1",
  175. CacheHit: true,
  176. CacheValidatedWithOriginServer: true,
  177. },
  178. SourceLocation: &logpb.LogEntrySourceLocation{
  179. File: "some_file.go",
  180. Line: 1,
  181. Function: "someFunction",
  182. },
  183. }
  184. got, err := fromLogEntry(&logEntry)
  185. if err != nil {
  186. t.Fatal(err)
  187. }
  188. if diff := testutil.Diff(got, want, cmpopts.IgnoreUnexported(http.Request{})); diff != "" {
  189. t.Errorf("FullEntry:\n%s", diff)
  190. }
  191. // Proto payload.
  192. alog := &audit.AuditLog{
  193. ServiceName: "svc",
  194. MethodName: "method",
  195. ResourceName: "shelves/S/books/B",
  196. }
  197. any, err := ptypes.MarshalAny(alog)
  198. if err != nil {
  199. t.Fatal(err)
  200. }
  201. logEntry = logpb.LogEntry{
  202. LogName: "projects/PROJECT_ID/logs/LOG_ID",
  203. Resource: res,
  204. Timestamp: ts,
  205. Payload: &logpb.LogEntry_ProtoPayload{ProtoPayload: any},
  206. }
  207. got, err = fromLogEntry(&logEntry)
  208. if err != nil {
  209. t.Fatal(err)
  210. }
  211. if !ltesting.PayloadEqual(got.Payload, alog) {
  212. t.Errorf("got %+v, want %+v", got.Payload, alog)
  213. }
  214. // JSON payload.
  215. jstruct := &structpb.Struct{Fields: map[string]*structpb.Value{
  216. "f": {Kind: &structpb.Value_NumberValue{NumberValue: 3.1}},
  217. }}
  218. logEntry = logpb.LogEntry{
  219. LogName: "projects/PROJECT_ID/logs/LOG_ID",
  220. Resource: res,
  221. Timestamp: ts,
  222. Payload: &logpb.LogEntry_JsonPayload{JsonPayload: jstruct},
  223. }
  224. got, err = fromLogEntry(&logEntry)
  225. if err != nil {
  226. t.Fatal(err)
  227. }
  228. if !ltesting.PayloadEqual(got.Payload, jstruct) {
  229. t.Errorf("got %+v, want %+v", got.Payload, jstruct)
  230. }
  231. }
  232. func TestListLogEntriesRequest(t *testing.T) {
  233. for _, test := range []struct {
  234. opts []EntriesOption
  235. resourceNames []string
  236. filter string
  237. orderBy string
  238. }{
  239. // Default is client's project ID, empty filter and orderBy.
  240. {nil, []string{"projects/PROJECT_ID"}, "", ""},
  241. {[]EntriesOption{NewestFirst(), Filter("f")},
  242. []string{"projects/PROJECT_ID"}, "f", "timestamp desc"},
  243. {[]EntriesOption{ProjectIDs([]string{"foo"})},
  244. []string{"projects/foo"}, "", ""},
  245. {[]EntriesOption{ResourceNames([]string{"folders/F", "organizations/O"})},
  246. []string{"folders/F", "organizations/O"}, "", ""},
  247. {[]EntriesOption{NewestFirst(), Filter("f"), ProjectIDs([]string{"foo"})},
  248. []string{"projects/foo"}, "f", "timestamp desc"},
  249. {[]EntriesOption{NewestFirst(), Filter("f"), ProjectIDs([]string{"foo"})},
  250. []string{"projects/foo"}, "f", "timestamp desc"},
  251. // If there are repeats, last one wins.
  252. {[]EntriesOption{NewestFirst(), Filter("no"), ProjectIDs([]string{"foo"}), Filter("f")},
  253. []string{"projects/foo"}, "f", "timestamp desc"},
  254. } {
  255. got := listLogEntriesRequest("projects/PROJECT_ID", test.opts)
  256. want := &logpb.ListLogEntriesRequest{
  257. ResourceNames: test.resourceNames,
  258. Filter: test.filter,
  259. OrderBy: test.orderBy,
  260. }
  261. if !proto.Equal(got, want) {
  262. t.Errorf("%v:\ngot %v\nwant %v", test.opts, got, want)
  263. }
  264. }
  265. }