16399 Commits

Author SHA1 Message Date
Joe Betz
bc0d8dad1e
Merge pull request #12380 from ptabor/20201009-move-single-integration
[important] tests/integration: Move misplaced integration test integration/v3_kv_test.go
2020-10-09 13:14:15 -04:00
Piotr Tabor
485030052a tests/integration: Move misplaces integration tests.
There was undetected 'conflict' between
  11ba1a610939218c6779a960f764b2bcfdd7fb83
and 2c66612e0ecb42abde6a4761cd708f5d285e0635.

Moving the file to proper location.
2020-10-09 09:39:55 +02:00
Josh Bleecher Snyder
24724af7f1
etcdserver: prevent crash using expvars when server is not running (#12376)
The raft.status expvar is added at init time.
This change ensures that evaluating that expvar variable
doesn't panic during evaluation, even when there is
no server running.
2020-10-08 17:37:25 -07:00
CFC4N
11ba1a6109
namespace: check IsWithFromKey if keyLen equal 0. (#12307)
* namespace: check IsWithFromKey if keyLen equal 0.

Rename function isWithFromKey/isWithPrefix to IsOptsWithFromKey/IsOptsWithPrefix.

fixes: #12282

* integration: add test while WithFromKey/WithPrefix called in opts.
2020-10-08 17:34:09 -07:00
Jingyi Hu
2c66612e0e
Merge pull request #12339 from ptabor/202009027-module-tests
Modularization: Establish 'testing' module (integration, functional, e2e)
2020-10-08 09:25:57 -07:00
Piotr Tabor
f67956cb7a clientv3: Expose clientv3/examples close to the code.
Many of the tests had missing '// Output:' comment, so were not
runnable. They required fining.
2020-10-08 14:27:32 +02:00
Piotr Tabor
dd45d04b2d clientv3/concurrency: Expose examples close to the source-code. 2020-10-07 15:46:56 +02:00
Piotr Tabor
c5ccebf792 tests/integration: Simplify the testMain for examples.
We introduce a LazyCluster abstraction (instead of copy-pasted logic)
that makes clusters to be created only if there are runnable tests
in need for the infrastructure.
2020-10-07 15:42:35 +02:00
Piotr Tabor
73b92fe688 tests: Make examples (for not client) to be both: documentation and integration-runnable
This CL tries to connect 2 objectives:
  - Examples should be close (the same package) to the original code,
    such that they can participate in documentation.
  - Examples should be runnable - such that they are not getting out of
    sync with underlying API/implementation.

In case of etcd-client, the examples are assuming running 'integration'
style, i.e. thay do connect to fully functional etcd-server.
That would lead to a cyclic dependencies between modules:
  - server depends on client (as client need to be lightweight)
  - client (for test purposes) depend on server.
Go modules does not allow to distingush testing dependency from
prod-code dependency.

Thus to meet the objective:
  - The examples are getting executed within testing/integration packages against real etcd
  - The examples are symlinked to 'unit' tests, such that they included in documentation.
  - Long-term the unit examples should get rewritten to use 'mocks' instead of real integration tests.
2020-10-07 15:38:40 +02:00
Piotr Tabor
313087f8c5 ./test,./scripts: Update go.sum and bill-of-materials generation logic (and refresh bom). 2020-10-07 15:38:39 +02:00
Piotr Tabor
9866c7e8ff .words: Expanded and resorted .words dictionary file.
Should fail following goword complains:
```
clientv3/config.go.48: // ("--max-request-bytes" flag to etcd or "embed.Config.MaxRequestBytes"). (spell: MaxRequestBytes -> ?)
clientv3/config.go.55: // ("--max-request-bytes" flag to etcd or "embed.Config.MaxRequestBytes"). (spell: MaxRequestBytes -> ?)
clientv3/leasing/doc.go.15: // Package leasing serves linearizable reads from a local cache by acquiring (spell: linearizable -> infeasible?)
clientv3/op.go.413: // it's linearizable. Serializable requests are better for lower latency (spell: linearizable -> infeasible?)
clientv3/retry.go.49: // an obvious server-side error (e.g. rpctypes.ErrRequestTooLarge). (spell: ErrRequestTooLarge -> Erectile?)
```
2020-10-07 15:38:39 +02:00
Piotr Tabor
7886ec8574 test: Modules: PASSES="integration" ./test understand new file locations 2020-10-07 15:38:39 +02:00
Piotr Tabor
be4e8b7013 integration/fixtures: Move the 'tests/integration/fixtures' directory up and update references.
I moved the files up as they are shared between e2e & integrational tests.
2020-10-07 15:38:28 +02:00
Piotr Tabor
bb9703820c tests/integration: Move of naming/snapshot clientv3 tests (and yaml reference)
path change in clientv3/yaml/config_test

git mv clientv3/naming/grpc_test.go         tests/integration/clientv3/grpc_test.go
git mv clientv3/snapshot/*_test.go          tests/integration/snapshot
git mv clientv3/{main_test.go,example_*.go} tests/integration/clientv3/examples/
2020-10-07 15:37:24 +02:00
Piotr Tabor
ffd4d5173c tests/mod.go: Update of go.mod after move of integration tests. 2020-10-07 15:37:24 +02:00
Piotr Tabor
b27b17b44c tests/functional: Mechanical rename of imports etcd/v3/integration/ -> etcd/tests/v3/integration/... 2020-10-07 15:37:21 +02:00
Piotr Tabor
3153038ffb tests/integration: Moving integration tests to tests/integration directory
git mv integration ./tests

git mv client/integration/* ./tests/integration/client
git mv clientv3/integration/* ./tests/integration/clientv3
git mv client/example_keys_test.go client/main_test.go tests/integration/client/examples
git mv clientv3/concurrency/*_test.go tests/integration/clientv3/concurrency

git mv etcdserver/api/v2store/store_v2v3_test.go etcdserver/api/v2v3/*_test.go tests/integration/v2store
git mv tests/integration/v2store/store_v2v3_test.go tests/integration/v2store/store_v2v3.go
git mv tests/integration/v2store/store_test.go tests/integration/v2store/store_v2v3_test.go
git mv etcdserver/api/v2store/store_test.go tests/integration/v2store
git mv etcdserver/api/v2store/store_v2_test.go tests/integration/v2store

git mv proxy/grpcproxy/*_test.go tests/integration/proxy/grpcproxy

git mv ./clientv3/snapshot/testdata ./clientv3/snapshot/*_test.go ./tests/integration/snapshot
2020-10-07 15:36:21 +02:00
Piotr Tabor
b382429d01 tests: Move functional tests to 'tests' module.
Tested with:

PASSES="fmt functional unit" ./test
make build-docker-functional
2020-10-07 15:09:26 +02:00
Piotr Tabor
8907b146d4 tests/functional: Mechanical rename of imports etcd/v3/functional/ -> etcd/tests/v3/functional/...
% find ./ -name "*.go" | xargs sed -i "s|go.etcd.io/etcd/v3/functional/agent|go.etcd.io/etcd/tests/v3/functional/agent|g"
% find ./ -name "*.go" | xargs sed -i "s|go.etcd.io/etcd/v3/functional/runner|go.etcd.io/etcd/tests/v3/functional/runner|g"
% find ./ -name "*.go" | xargs sed -i "s|go.etcd.io/etcd/v3/functional/tester|go.etcd.io/etcd/tests/v3/functional/tester|g"
% find ./ -name "*.go" | xargs sed -i "s|go.etcd.io/etcd/v3/functional/rpcpb|go.etcd.io/etcd/tests/v3/functional/rpcpb|g"
2020-10-07 15:09:12 +02:00
Piotr Tabor
69254d8cf8 functional,tests: git mv functional tests/
Modularization: Mechanical move of "functional" directory into tests module using:

% git mv functional tests/
2020-10-07 15:03:51 +02:00
Piotr Tabor
4fb48b7c14 tests: Estabilishing module for tests
For now it contains only e2e tests, but integration & functional will follow.
2020-10-07 15:03:51 +02:00
Jingyi Hu
0aab02e7b5
Merge pull request #12367 from ptabor/20201005-api2client
Modularization: Move dependencies of client (protos, version) to api/ module
2020-10-07 05:56:11 -07:00
Piotr Tabor
997961ebfd bom: Update bill of materials to reflect the new module. 2020-10-06 12:28:40 +02:00
Piotr Tabor
ec3026fdc9 *: Run ./scripts/genproto.sh (protoc 3.12.3) after proto file moves.
The changed blobs are consequences of proto-descriptors changing as a
result of file moves.
2020-10-06 11:57:19 +02:00
Piotr Tabor
28f2b07623 *: Update references to code moved to the api/ dir.
Follow up to file-moves done in the previous commit.

The commit contains purely mechanical consequences of execution (apart
of scripts/genproto.sh):

  % find ./ -name '*.go'  | xargs sed --follow-symlinks -i 's|v3/etcdserver/api/v3rpc/rpctypes|v3/api/v3rpc/rpctypes|g'
  % find ./ -name '*.go'  | xargs sed --follow-symlinks -i 's|v3/version|v3/api/version|g'
  % find ./ -name '*.go'  | xargs sed --follow-symlinks -i 's|v3/mvcc/mvccpb|v3/api/mvccpb|g'
  % find ./ -name '*.go'  | xargs sed --follow-symlinks -i 's|v3/etcdserver/etcdserverpb|v3/api/etcdserverpb|g'
  % find ./ -name '*.go'  | xargs sed --follow-symlinks -i 's|v3/etcdserver/api/membership/membershippb|v3/api/membershippb|g'
  % find ./ -name '*.go'  | xargs sed --follow-symlinks -i 's|v3/auth/authpb|v3/api/authpb|g'

  % find ./ -name '*.proto' -o -name '*.md'  | xargs -L 1 sed --follow-symlinks -i 's|/mvcc/mvccpb/kv.proto|/api/mvccpb/kv.proto|g'
  % find ./ -name '*.proto' -o -name '*.md'  | xargs -L 1 sed --follow-symlinks -i 's|/auth/authpb/auth.proto|/api/authpb/auth.proto|g'
  % find ./ -name '*.proto' -o -name '*.md'  | xargs -L 1 sed --follow-symlinks -i 's|/etcdserver/api/membership/membershippb/membership.proto|/api/membershippb/membership.proto|g'

  I also modified manually paths in scripts/genproto.sh.

  % go fmt ./...
2020-10-06 11:56:16 +02:00
Piotr Tabor
2edb08642c api: Make api/ a module that will contain proto-definitions.
The module is supposed to contain minimal set of files that establish
public etcd server API. In particular client libraries for etcd built in
different languages might want to depend on this file.
2020-10-06 11:54:50 +02:00
Piotr Tabor
389642dd16 client: Move client specific code (protos, version) to api/
client: Move client specific code (protos, version) to the api/
directory. Thanks to this change /client directory will not need to depend on
the server code. In next commits we make "/api" a module on its own.

Mechanical consequences of execution:

% git mv version/version.go api/version
% git mv etcdserver/api/v3rpc/rpctypes api/v3rpc
% git mv mvcc/mvccpb api/
% git mv etcdserver/etcdserverpb api/
% git mv auth/authpb api/
% git mv etcdserver/api/membership/membershippb api/
2020-10-06 11:53:36 +02:00
Sahdev Zala
7bd956fa2b
Merge pull request #12366 from guusvw/fix-yaml-indention-doc
the example alert file had a wrong indentation
2020-10-05 14:08:12 -04:00
Guus van Weelden
985d4cffc4
Documentation: the example alert file had a wrong indentation
Signed-off-by: Guus van Weelden <guus.vanweelden@moia.io>
2020-10-05 18:11:21 +02:00
Sahdev Zala
0693e2b4df
Merge pull request #12355 from cfc4n/changelog_gettoken
CHANGELOG: update for #12165 , #12264 .
2020-10-05 11:14:09 -04:00
Gyuho Lee
fdb3f89730
Merge pull request #12362 from ptabor/20201001-deflake-unit-race
Fix "race" - auth unit tests leaking goroutines
2020-10-04 20:47:52 -07:00
Piotr Tabor
97820f1c6e integration: Fix flakes of TestV3WatchRestoreSnapshotUnsync
```
```

The flakes manifested as:
```
--- FAIL: TestV3WatchRestoreSnapshotUnsync (3.59s)
    v3_watch_restore_test.go:82: inflight snapshot sends expected 0 or 1, got ""
FAIL
coverage: 55.2% of statements
FAIL	go.etcd.io/etcd/v3/integration	3.646s
FAIL
```

The root reason is that all the SnapMsg processing happends on both ends
(leader, follower) assynchronously in goroutines, e.g. on Fifo schedule
within EtcdServer.run, so when we observe through metrics, we don't
know whether it finised (or even got started).

Idally we should have EtcdServer.Drain() call that exits when the server
processed or internal 'queues' and is idle.
2020-10-03 19:39:08 +02:00
Piotr Tabor
98b123f034 mvcc: Fix races between metrics gathering and mvcc.Restore
The races was manifesting as following flakes:

```
```
See:
  https://github.com/etcd-io/etcd/issues/12336

I'm taking the locks for short-duration of time (instead of the whole
duriation of Restore) to allow metrics being gather when the server
restoration is in progress.

```
{"level":"warn","ts":"2020-09-26T13:33:13.010Z","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"endpoint://client-c9c21e47-2013-4776-8e83-e331b2caa9ae/localhost:14422410081761184170","attempt":0,"error":"rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = \"transport: Error while dialing dial unix localhost:14422410081761184170: connect: no such file or directory\""}
{"level":"warn","ts":"2020-09-26T13:33:13.011Z","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"endpoint://client-c9c21e47-2013-4776-8e83-e331b2caa9ae/localhost:14422410081761184170","attempt":0,"error":"rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = \"transport: Error while dialing dial unix localhost:14422410081761184170: connect: no such file or directory\""}
{"level":"warn","ts":"2020-09-26T13:33:16.285Z","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"endpoint://client-b504e954-e000-42a4-aa4f-70ded8dbef39/localhost:55672762955698614610","attempt":0,"error":"rpc error: code = NotFound desc = etcdserver: requested lease not found"}
{"level":"warn","ts":"2020-09-26T13:33:21.434Z","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"endpoint://client-7945004b-f67e-42aa-af11-a7b40fbbe6fc/localhost:49623072144007561240","attempt":0,"error":"rpc error: code = Canceled desc = context canceled"}
==================
WARNING: DATA RACE
Write at 0x00c000905f78 by goroutine 764:
  go.etcd.io/etcd/v3/mvcc.(*store).restore()
      /go/src/go.etcd.io/etcd/mvcc/kvstore.go:397 +0x773
  go.etcd.io/etcd/v3/mvcc.(*store).Restore()
      /go/src/go.etcd.io/etcd/mvcc/kvstore.go:343 +0x5f1
  go.etcd.io/etcd/v3/mvcc.(*watchableStore).Restore()
      /go/src/go.etcd.io/etcd/mvcc/watchable_store.go:199 +0xe2
  go.etcd.io/etcd/v3/etcdserver.(*EtcdServer).applySnapshot()
      /go/src/go.etcd.io/etcd/etcdserver/server.go:1107 +0xa49
  go.etcd.io/etcd/v3/etcdserver.(*EtcdServer).applyAll()
      /go/src/go.etcd.io/etcd/etcdserver/server.go:1031 +0x6d
  go.etcd.io/etcd/v3/etcdserver.(*EtcdServer).run.func8()
      /go/src/go.etcd.io/etcd/etcdserver/server.go:986 +0x53
  go.etcd.io/etcd/v3/pkg/schedule.(*fifo).run()
      /go/src/go.etcd.io/etcd/pkg/schedule/schedule.go:157 +0x11e
Previous read at 0x00c000905f78 by goroutine 180:
  [failed to restore the stack]
Goroutine 764 (running) created at:
  go.etcd.io/etcd/v3/pkg/schedule.NewFIFOScheduler()
      /go/src/go.etcd.io/etcd/pkg/schedule/schedule.go:70 +0x2b1
  go.etcd.io/etcd/v3/etcdserver.(*EtcdServer).run()
      /go/src/go.etcd.io/etcd/etcdserver/server.go:871 +0x32c
Goroutine 180 (running) created at:
  net/http.(*Server).Serve()
      /usr/local/go/src/net/http/server.go:2933 +0x5b6
  net/http/httptest.(*Server).goServe.func1()
      /usr/local/go/src/net/http/httptest/server.go:308 +0xd3
==================
--- FAIL: TestV3WatchRestoreSnapshotUnsync (6.74s)
    testing.go:906: race detected during execution of test
FAIL
coverage: 83.5% of statements
FAIL	go.etcd.io/etcd/v3/integration	231.272s
FAIL
Command 'go test -timeout=30m -cpu=1 --race --cover=true go.etcd.io/etcd/v3/integration' failed.
```
2020-10-03 19:39:08 +02:00
Piotr Tabor
220f711a2a clientv3/integration: Fix leaked goroutine in case of skipped test. 2020-10-03 19:38:54 +02:00
Piotr Tabor
528f5315d6 auth: Fix "race" - auth unit tests leaking goroutines
- We were leaking goroutines in auth-test
  - The go-routines were depending / modifying global test environment
variables (simpleTokenTTLDefault) leading to races

Removed the leaked go-routines, and expanded 'auth' package to
be covered we leaked go-routines detection.
2020-10-03 19:38:30 +02:00
Piotr Tabor
0e5d81704f .travis.yml, scripts: Fix minor bugs in the test script.
1. setting environment variable cannot be in quote
2. "--race" testing for unit tests is supposed to be part of linux-amd64-unit-4-cpu-race config.
3. 'run' function in test script should log_error in case of failed
command (wrong operator for ints comparison in bash).
2020-10-01 14:34:58 +02:00
CFC4N
b0e8ec951c
CHANGELOG: update for #12165 , #12264 . 2020-09-30 16:57:15 +08:00
vivian
ab4cc3caef
raft/log: remove redundant code logic (#12346)
Remove redundant code logic

Co-authored-by: yangweiwei <yangweiwei@cmss.chinamobile.com>
2020-09-29 19:48:32 -07:00
Piotr Tabor
a6b0375b7b
Travis: Reduce footprint of unit tests - so hopefully flakiness (#12350)
* ./tests: Remove legacy coverage collection code

The legacy tests/cover.test.bash script was not ./test script
compatible for a long time.

The following method of coverage collection works (also across
packages) and does not make all the test execution slower.

```
COVERDIR=coverage PASSES="build build_cov cov" ./test
go tool cover -html ./coverage/cover.out
```

* CI: Reduce duplicated coverage between different variants on Travis

We used to execute unit tests in 3 different jobs,
every time with --race detection and every time in 3 variants:1,2,4
CPUS.

The proposed change makes each of the jobs use different variant of
CPUS, and only 4-cpu variant is running with --race detection
(as the more-parallel variant is more likely to experience races),
2020-09-29 19:47:05 -07:00
Joe Betz
b47cd2f470
Merge pull request #12322 from ptabor/20200920-test-script
./test: Refactoring of test script for modularization
2020-09-29 11:09:24 -04:00
Piotr Tabor
b3bbe10465 go.sum: Update & make sure PASSES="mod_tidy" ./test detects such problems.
Commit inspired by this failure:
  https://travis-ci.com/github/etcd-io/etcd/jobs/391164537

This is not happanning locally - but can be forced by removal of go.sum
file. Let's watch how frequently we will need to refresh go.sum.
2020-09-28 12:00:25 +02:00
Piotr Tabor
f1d4593241 ./test: Refactoring of test script for modularization
This refactoring offers following benefits:

  - Unified way how go test commands are being called (in terms of flags intepretation)
  - Uses standard go mechanisms (like go lists) to find files/packages that are subject for test. The mechanism are module aware.
  - Added instruction how to install tools needed for the tests/checkers.
  - Added colors to the output to make it easier to spot any failure.

Confirmed to work using:
- COVERDIR="./coverage" CPU="4" RACE=false COVER=false PASSES="build build_cov cov" ./test
- CPU="4" RACE=false COVER=false PASSES="e2e functional integration" ./test
- COVERDIR="./coverage" COVER="false" CPU="4" RACE="false" PASSES="fmt build unit build_cov integration e2e integration_e2e grpcproxy cov" ./test
- PASSES=unit PKG=./wal TIMEOUT=1m ./test
- PASSES=integration PKG=./clientv3 TIMEOUT=1m ./test
- PASSES=unit PKG=./wal TESTCASE=TestNew TIMEOUT=1m ./test
- PASSES=unit PKG=./wal TESTCASE="\bTestNew\b" TIMEOUT=1m ./test
- PASSES=integration PKG=./client/integration TESTCASE="\bTestV2NoRetryEOF\b" TIMEOUT=1m ./test
- COVERDIR=coverage PASSES="build_cov cov" ./test
2020-09-28 11:07:50 +02:00
Pierre Zemb
cc2b4cd05e
etcdserver: add more detailed traces on linearized reading (#12335)
To improve debuggability of `agreement among raft nodes before
linearized reading`, we added some tracing inside
`linearizableReadLoop`.

This will allow us to know the timing of `s.r.ReadIndex` vs
`s.applyWait.Wait(rs.Index)`.
2020-09-26 19:08:36 -07:00
Piotr Tabor
31426b0041 clientv3/ordering: Split mocked part of the test from integration-level test. 2020-09-26 08:44:58 +02:00
Piotr Tabor
d65b5d6791 Makefile: Improve the 'make clean' to remove all the tmp artifacts 2020-09-25 22:20:52 +02:00
Piotr Tabor
16eeedffaa pkg/testutil: Fixing flakes due to >>leak" text/template/parse goroutines.
Examplar flake: https://travis-ci.com/github/etcd-io/etcd/jobs/388806782
```
go test -timeout=5m -cpu=1 --run=Example ./client/...

ok  	go.etcd.io/etcd/v3/client	0.085s
testing: warning: no tests to run
PASS
Unexpected goroutines running after all test(s).
1 instances of:
text/template/parse.(*lexer).emit(...)
	/usr/local/go/src/text/template/parse/lex.go:157
text/template/parse.lexText(...)
	/usr/local/go/src/text/template/parse/lex.go:269 +0x4f0
text/template/parse.(*lexer).run(...)
	/usr/local/go/src/text/template/parse/lex.go:230 +0x37
created by text/template/parse.lex
	/usr/local/go/src/text/template/parse/lex.go:223 +0x190
FAIL	go.etcd.io/etcd/v3/client/integration	0.013s
```
2020-09-25 22:10:43 +02:00
Piotr Tabor
73e5714bc5
integration: 'go test -tags cluster_proxy -v ./integration/... ./clientv3/...' passes now. (#12319)
The grpc-proxy test logic was assuming that the context associated to client is closed,
while in practice all tests called client.Close() without explicit context close.

The current testing strategy is complicated 2 fold:
  - grpc proxy works like man-in-the middle of each Connection issues
from integration tests and its lifetime is bound to the connection.
  - both connections (client -> proxy, and proxy -> etcd-server) are
represented by the same ClientV3 object instance (with substituted
implementations of KV or watcher).

The fix splits context representing proxy from context representing proxy -> etcd-server connection,
thus allowing cancelation of the proxy context.
2020-09-25 12:18:58 -07:00
mlmhl
3f36143790
pkg/traceutil: skip subTraceStart/subTraceEnd steps when logging steps (#12262)
SubTraceStart and SubTraceEnd steps are only placeholders, not really
steps, we should skip them when logging the long duration steps,
otherwise these steps will lead to incorrect start time and duration
 of subsequent steps.
2020-09-25 11:46:06 -07:00
Piotr Tabor
7bf75824bf
CHANGELOG: Update changelog about modules instead of ./vendor (#12313)
Follow up to PR: https://github.com/etcd-io/etcd/pull/12279
2020-09-25 11:29:51 -07:00
Naga Ravi Chaitanya Elluri
ed82418799
Documentation: Add etcd database quota alerts (#12249)
This commit:
- Fires a critical alert when the etcd database quota is 95% full
  at any given point of time to alert the user to defrag or increase
  the quota in order to avoid the alarm getting triggered which blocks
  all the writes to etcd meaning there can't be any new objects created.
  This is needed to make sure the cluster supports running large number
  of nodes and objects.
- Fires a warning when there is a sudden surge in etcd writes leading to
  increase in the etcd database quota size at an alarming rate as it
  is disruptive. It might be because of a rougue process and it's
  important to alert the admin.
2020-09-25 11:03:04 -07:00