mirror of
https://github.com/googleforgames/open-match.git
synced 2025-04-08 10:27:22 +00:00
Compare commits
885 Commits
v0.1.0-alp
...
v1.1.0
Author | SHA1 | Date | |
---|---|---|---|
02ce5e26b7 | |||
32611444d6 | |||
c0b355da51 | |||
6f05e526fb | |||
496d156faa | |||
3a3d618c43 | |||
e1cbd855f5 | |||
10b36705f0 | |||
a6fc4724bc | |||
511337088a | |||
5f67bb36a6 | |||
94d2105809 | |||
d85f1f4bc7 | |||
79e9afeca7 | |||
3334f7f74a | |||
85ce954eb9 | |||
679cfb5839 | |||
c53a5b7c88 | |||
cfb316169a | |||
a9365b5333 | |||
93df53201c | |||
eb86841423 | |||
771f706317 | |||
a9f9a2f2e6 | |||
068632285e | |||
113461114e | |||
0ac7ae13ac | |||
29a2dbcf99 | |||
48d3b5c0ee | |||
a5fa651106 | |||
cd84d74ff9 | |||
8c2aa1ea81 | |||
493ff8e520 | |||
8363bc5fc9 | |||
144f646b7f | |||
b518b5cc1b | |||
af0b9fd5f7 | |||
5f4b522ecd | |||
12625d7f53 | |||
3248c8c4ad | |||
10c0c59997 | |||
c17e3e62c0 | |||
8e91be6201 | |||
f6c837d6cd | |||
3c8908aae0 | |||
0689d92d9c | |||
3c9a8f5568 | |||
30204a2d15 | |||
a5b6c0c566 | |||
4a00baf847 | |||
d74262f3ba | |||
2262652ea9 | |||
e15fd47535 | |||
670f38d36e | |||
f0a85633a5 | |||
6cb47ce191 | |||
529c01330e | |||
b36a348db7 | |||
5e277265ad | |||
4420d7add2 | |||
3de052279b | |||
7a4aa3589f | |||
bca6f487cc | |||
d0c373a850 | |||
deb2947ae2 | |||
d889278151 | |||
1b63fa53dc | |||
af02e4818f | |||
cda2d3185f | |||
2317977602 | |||
9ef83ed344 | |||
33bd633b1d | |||
1af8cf1e79 | |||
0ef46fc4d4 | |||
79daf50531 | |||
a9c327b430 | |||
2c637c97b8 | |||
668b10030b | |||
1c7fd24a34 | |||
be0cebd457 | |||
fe7bb4da8f | |||
e80de171a0 | |||
fdd707347e | |||
6ef1382414 | |||
d67a65e648 | |||
d3e008cd1e | |||
d93db94ad9 | |||
1bd63a01c7 | |||
cf8d49052c | |||
fca5359eee | |||
07637135a9 | |||
8c86a4e643 | |||
31858e0ce5 | |||
fc0b6dc510 | |||
edade67a6d | |||
c92c4ef07a | |||
0b8425184b | |||
338a03cce5 | |||
b7850ab81d | |||
faa730bda8 | |||
76ef9546af | |||
bff8934cd3 | |||
3a5608b547 | |||
b7eec77a36 | |||
82a011ea52 | |||
92210b1a13 | |||
f46c0b8f3d | |||
a19baf3457 | |||
8e1fbaf938 | |||
957471cf83 | |||
e24c4b9884 | |||
34cc4987e8 | |||
8e8f2d688b | |||
f347639df4 | |||
75c74681cb | |||
5b18dcf6f3 | |||
3bcf327a41 | |||
9f59844e0d | |||
5a32cef2e9 | |||
b9e2e88ef4 | |||
41632e6b8d | |||
188457c21f | |||
4daea744d5 | |||
1f3dd4bcbf | |||
d82fc4fec6 | |||
8cb43950a1 | |||
9934a7e9da | |||
8db449b307 | |||
b78d4672a6 | |||
e048b97c71 | |||
f56263b074 | |||
aaca99c211 | |||
9c1b0bcc0e | |||
80675c32f6 | |||
4e408b1abc | |||
fd4f154a0e | |||
3e2d20edc0 | |||
40ba558eb2 | |||
72bcd72d5c | |||
b276ed1a08 | |||
d977486dc5 | |||
1f74497bdd | |||
57e9540faa | |||
a0be7dcec5 | |||
391cc4dc72 | |||
2c8779c5d7 | |||
e5aafc5ed7 | |||
8554601a70 | |||
a75833b85a | |||
f01105995d | |||
f949de7dce | |||
335bf73904 | |||
7a1dcbdf93 | |||
0a65bdefe5 | |||
bcf0e6b9fb | |||
1f5df7abef | |||
7005d40939 | |||
3536913559 | |||
103213f940 | |||
3b8efce53d | |||
580ed235d7 | |||
23cc35ae68 | |||
c002e75fde | |||
6e6f063958 | |||
8d31b5af07 | |||
f1a5cd9b81 | |||
d3d906c8be | |||
6068507370 | |||
04b06fcf90 | |||
0c25ac9139 | |||
0565a014ad | |||
57e59c3821 | |||
608d5bce71 | |||
52b8754eb8 | |||
a10817f550 | |||
817a0968e7 | |||
043a984bab | |||
02d8d1f1fe | |||
242d799c18 | |||
33189f9154 | |||
9ef7cb6277 | |||
b05c9f5574 | |||
8a29f15fe0 | |||
5fa0cc700c | |||
d579de63aa | |||
797352a3fc | |||
68882a79bb | |||
11bf81e146 | |||
02aa992ac7 | |||
f5b651669c | |||
b9522a8bb5 | |||
8c6fbcbe49 | |||
99141686c9 | |||
3cf9c2ad6a | |||
755c0e82f1 | |||
18bc9f31fd | |||
05325d3b77 | |||
3c7f73ed03 | |||
525d35b341 | |||
d3e8638a3b | |||
af19404eef | |||
2f9e1c2209 | |||
669f7d63b7 | |||
8740494f3e | |||
3899bd2fcd | |||
dac6ac141e | |||
c859e04bf9 | |||
7a48467cb5 | |||
74992cdf79 | |||
dd21919c00 | |||
031b39e9c2 | |||
3f8f858d85 | |||
1dbd3a5a45 | |||
fc94a7c451 | |||
60d20ebae5 | |||
e369ac3c0b | |||
2c35ecb304 | |||
7350524e78 | |||
2aee5d128d | |||
4773b7b7cf | |||
1abdace01e | |||
bbcf8d47b4 | |||
d65dee6be0 | |||
02e6b3bbde | |||
77090d1a5b | |||
ce3a7bf389 | |||
bcf710b755 | |||
2b7eec8c07 | |||
99164df2db | |||
efa1ce5a0b | |||
cb610a92b1 | |||
89691b5512 | |||
252d473d72 | |||
56cfb8e66e | |||
5f32d4b765 | |||
658aee8874 | |||
6ac7910fb1 | |||
fcf7c81c84 | |||
e3d630729c | |||
da9d48ddb1 | |||
3727b0d5d8 | |||
cc1b70dd2e | |||
91090af431 | |||
6661df62ae | |||
f63a93b139 | |||
31648c35f3 | |||
e96a6e8af7 | |||
d9912c3e28 | |||
8933255ec2 | |||
3617d3cdbd | |||
736a979b47 | |||
aa99d7860e | |||
22ad6fed6b | |||
39e495512b | |||
291e60a735 | |||
b29dfae9cf | |||
377b40041d | |||
6f7b7640c2 | |||
039eefb690 | |||
5de0ae1fc4 | |||
1e5560603a | |||
61449fe2cf | |||
21cf0697fe | |||
12e5a37816 | |||
e658cc0d84 | |||
9e89735d79 | |||
5cbbfef1cc | |||
1c0e4ff94e | |||
79862c9950 | |||
8ac27d7975 | |||
86b8cb5aa8 | |||
fdea3c8f1e | |||
61a28df3e5 | |||
13fe3fe5a9 | |||
a674fb1c02 | |||
75ffc83b98 | |||
7dc4de6a14 | |||
f02283e2a6 | |||
d1fe7f1ac4 | |||
84eb9b27ef | |||
707de22912 | |||
780e3abf10 | |||
524b7d333f | |||
c544b9a239 | |||
04b6f1a5ad | |||
13952ea54e | |||
a61f4a643e | |||
949fa28505 | |||
85cc481f5d | |||
c3cbcd7625 | |||
e01fc12549 | |||
e1682100fa | |||
603aef207f | |||
baf403ac44 | |||
b1da77eaba | |||
bb82a397d2 | |||
abd2c1434c | |||
bc9dc27210 | |||
084461d387 | |||
bc7d014db6 | |||
230ae76bb4 | |||
ebbe5aa6ce | |||
9b350c690c | |||
80b817f488 | |||
df7021de1b | |||
5c8f218000 | |||
3f538df971 | |||
1e856658c9 | |||
eb6697052d | |||
31d3464a31 | |||
c96b65d52b | |||
9d601351cc | |||
7272ca8b93 | |||
b463d2e0fd | |||
07da543f8e | |||
0d54c39828 | |||
5469c8bc69 | |||
c837211cd1 | |||
5729e72214 | |||
66910632da | |||
c832074112 | |||
a6d526b36b | |||
13e017ba65 | |||
3784300d22 | |||
31fd18e39b | |||
a54d1fcf21 | |||
72a435758e | |||
6848fa71c2 | |||
987d90cc44 | |||
baf943fdd3 | |||
c7ce1b047b | |||
36a194e761 | |||
605511d177 | |||
2a08732508 | |||
e1c2b96cb5 | |||
1bd84355b7 | |||
a9014fbf78 | |||
8050c61618 | |||
8b765871c4 | |||
88786ecbd1 | |||
f41c175f29 | |||
3607809371 | |||
d21ae712a7 | |||
f3f1908318 | |||
9cb4a9ce6e | |||
8dad7fd7d0 | |||
f70cfee14a | |||
36f92b4336 | |||
164dfdde67 | |||
c0d6531f3f | |||
52610974de | |||
041572eef6 | |||
e28fe42f3b | |||
880e340859 | |||
88a659544e | |||
9381918163 | |||
1c41052bd6 | |||
819ae18478 | |||
ad96f42b94 | |||
1d778c079c | |||
4bbfafd761 | |||
28e5d0a1d1 | |||
a394c8b22e | |||
93276f4d02 | |||
310d98a078 | |||
a84eda4dab | |||
ce038bc6dd | |||
74fb195f41 | |||
1dc3fc8b6b | |||
de469cb349 | |||
7462f32125 | |||
3268461a21 | |||
3897cd295e | |||
5a9212a46e | |||
04cfddefd0 | |||
b7872489ae | |||
6d65841b77 | |||
b4fb725008 | |||
50d9a0c234 | |||
a68fd5ed1e | |||
be58fae864 | |||
f22ad9afc5 | |||
6b1b84c54e | |||
043ffd69e3 | |||
e5f7d3bafe | |||
8f88ba151e | |||
9c83062a41 | |||
7b31bdcedf | |||
269e6cd0ad | |||
864f13f2e8 | |||
e3a9f59ad9 | |||
8a3f6e43b8 | |||
2b8597c72e | |||
c403f28c04 | |||
317f914daa | |||
16fbc015b2 | |||
d1d9114ddb | |||
e6622ff585 | |||
99fb4a8fcf | |||
e0ebb139bf | |||
31dcbe39f7 | |||
76a1cd8427 | |||
ac6c00c89d | |||
a7d97fdf0d | |||
f08121cf25 | |||
cd6dd410ee | |||
5f0a2409e8 | |||
d445a0b2d5 | |||
1526827e3c | |||
82e60e861f | |||
5900a1c542 | |||
a02aa99c7a | |||
2f3f8b7f56 | |||
a7eb1719cc | |||
ea24b702c8 | |||
e7ab30dc63 | |||
8b88f26e4e | |||
d5f60ae202 | |||
113ee00a6c | |||
c083f1735a | |||
52ad8de602 | |||
3daebfc39d | |||
3e5da9f7d5 | |||
951e82b6a2 | |||
d201242610 | |||
1328a109e5 | |||
2415194e68 | |||
b2214f7b9b | |||
98220fdc0b | |||
b2bf00631a | |||
49ac68c32a | |||
7b3d6d38d3 | |||
a1271ff820 | |||
2932144d80 | |||
3a14bf3641 | |||
7d0ec363e5 | |||
dcff6326b1 | |||
ffd77212b0 | |||
ea3e529b0d | |||
db2c298a48 | |||
85d5f9fdbb | |||
401329030a | |||
d9e20f9c29 | |||
f95164148f | |||
ab39bcc93d | |||
d1ae3e9620 | |||
de83c9f06a | |||
9fb445fda6 | |||
050367eb88 | |||
40d288964b | |||
e4c87c2c3a | |||
bd2927bcc5 | |||
271e745a61 | |||
98c15e78ad | |||
fbbe3cd2b4 | |||
878ef89c40 | |||
a9a5a29e58 | |||
8cd7cd0035 | |||
92495071d2 | |||
a766b38d62 | |||
6c941909e8 | |||
77dc8f8c47 | |||
a804e1009b | |||
3b2efc39c7 | |||
b8054633bf | |||
336fad9079 | |||
ce59eedd29 | |||
83c0913c34 | |||
6b50cdd804 | |||
f427303505 | |||
269dd9bc2f | |||
d501dbcde6 | |||
04c4e376b5 | |||
3e61359f05 | |||
8275ed76c5 | |||
e8b2525262 | |||
3517b7725c | |||
6cd521abf7 | |||
924fccfeb3 | |||
c17ca7a10c | |||
b11863071f | |||
20dbcea99f | |||
13505956a0 | |||
3d04025860 | |||
d0f7f2c4d3 | |||
272e7642b1 | |||
f3f80a70bd | |||
80bcd9487f | |||
2e25daf474 | |||
9fe32eef96 | |||
0446159872 | |||
2ef8614687 | |||
de8279dfe0 | |||
8fedc2900f | |||
0f95adce20 | |||
4f851094a0 | |||
9cae854771 | |||
603089f404 | |||
d024b46487 | |||
a2616870c7 | |||
6d8b516026 | |||
b4d3e84e3d | |||
6b370f56c8 | |||
d5da3d16b7 | |||
31469bb0f9 | |||
e8adc57f76 | |||
0da8d0d221 | |||
08d9210588 | |||
8882d8c9a1 | |||
6c65e924ec | |||
beba937ac5 | |||
caa755272b | |||
ea60386fa0 | |||
9000ae8de4 | |||
4e2b64722f | |||
4c0f24217f | |||
872b7be6a5 | |||
53f2ee208f | |||
b5eaf153e8 | |||
d3c7eb2000 | |||
23243e2815 | |||
3be97908b2 | |||
5892f81214 | |||
40892c9b2e | |||
9691d3f001 | |||
9808066375 | |||
534716eef4 | |||
ece4a602d0 | |||
8eb72d98b2 | |||
6da8a34b67 | |||
b03189e34c | |||
9766871a87 | |||
4eac4cb29a | |||
439286523d | |||
e0058c7c08 | |||
ec40f26e62 | |||
17134f0a40 | |||
add2464b33 | |||
b72b4f9b54 | |||
abdc3aca28 | |||
3ab724e848 | |||
3c8d0ce1b0 | |||
c0166e3176 | |||
3623adb90e | |||
fba1bcf445 | |||
fdd865200a | |||
b0fc8f261f | |||
bdd3503d80 | |||
81fd2bab83 | |||
212a416789 | |||
2425e28129 | |||
3993a2f584 | |||
05cb4e503f | |||
1cf11e7d81 | |||
1985ecefed | |||
b7ebb60325 | |||
e4651d9382 | |||
04a574688a | |||
d4a901fc71 | |||
5de79f90cf | |||
e42c8a0232 | |||
1503ffae3a | |||
a842da5563 | |||
c3d6efef72 | |||
0516ab0800 | |||
668bfd6104 | |||
ef933ed6ef | |||
3ee24e3f28 | |||
d0bd794a61 | |||
37bbf470de | |||
412cb8557a | |||
38e81a9fd1 | |||
cb24baf107 | |||
c6f6526823 | |||
41e441050f | |||
235e25c748 | |||
93ca5e885c | |||
5d67fb1548 | |||
faa6e17607 | |||
6a0c648a8f | |||
8516e3e036 | |||
e476078b9f | |||
0d405df761 | |||
06c1208ab2 | |||
af335032a8 | |||
a8be8afce2 | |||
e524121b4b | |||
871abeee69 | |||
b9af86b829 | |||
6a9572c416 | |||
636eb07869 | |||
d56c983c17 | |||
a8e857c0ba | |||
75da6e1f4a | |||
e1fba5c1e8 | |||
d9911bdfdd | |||
175293fdf9 | |||
01407fbcad | |||
edad339a76 | |||
c57c841dfc | |||
54dc0e0518 | |||
fa4e8887d0 | |||
8384cb00b2 | |||
b9502a59a0 | |||
139d345915 | |||
5ea5b29af4 | |||
812afb2d06 | |||
cc82527eb5 | |||
80b20623fb | |||
a270eab4b4 | |||
36f7dcc242 | |||
a4706cbb73 | |||
c09cc8e27f | |||
be55bfd1e8 | |||
8389a62cf1 | |||
af8895e629 | |||
2a3241307f | |||
f777a4f407 | |||
88ca8d7b7c | |||
3a09ce142a | |||
8d8fdf0494 | |||
45b0a7c38e | |||
4cbee9d8a7 | |||
55afac2c93 | |||
8077dbcdba | |||
f1fc02755b | |||
0cce1745bc | |||
d57b5f1872 | |||
1355e5c79e | |||
4809f2801f | |||
68d323f3ea | |||
b99160e356 | |||
98d4c31c61 | |||
b4beb68920 | |||
b41a704886 | |||
88a692cdf3 | |||
623519bbb4 | |||
655abfbb26 | |||
ac81b74fad | |||
ba62520d9c | |||
0205186e6f | |||
ef2b1ea0a8 | |||
1fe2bd4900 | |||
5333ef2092 | |||
09b727b555 | |||
c542d6d1c3 | |||
8f3f7625ec | |||
6a4f309bd5 | |||
26f5426b61 | |||
f464b0bd7b | |||
092b7e634c | |||
454a3d6cca | |||
50e3ede4b9 | |||
6c36145e9b | |||
47644004db | |||
1dec4d7555 | |||
1c6f43a95f | |||
0cea4ed713 | |||
db912b2d68 | |||
726b1d4063 | |||
468aef3835 | |||
c6e257ae76 | |||
8e071020fa | |||
c032e8f382 | |||
2af432c3d7 | |||
4ddceb62ee | |||
ddb4521444 | |||
86918e69eb | |||
2d6db7e546 | |||
fc52ef6428 | |||
1bfb30be6f | |||
9ee341baf2 | |||
7869e6eb81 | |||
7edca56f56 | |||
eaedaa0265 | |||
9cc8312ff5 | |||
2f0a1ad05b | |||
2ff77ac90b | |||
2a3cfea505 | |||
b8326c2a91 | |||
ccc9d87692 | |||
bba49f3ec4 | |||
632157806f | |||
6e039cb797 | |||
8db062f7b9 | |||
f379a5eb46 | |||
f3160cfc5c | |||
442a1ff013 | |||
0fb75ab35e | |||
6308b218cc | |||
624ba5c018 | |||
82d034f8e4 | |||
97eed146da | |||
6dd23ff6ad | |||
03c7db7680 | |||
e5538401f6 | |||
eaa811f9ac | |||
3b1c6b9141 | |||
34f9eb9bd3 | |||
3ad7f75fb4 | |||
78bd48118d | |||
3e71894111 | |||
36decb4068 | |||
f79b782a3a | |||
db186e55ff | |||
957465ce51 | |||
478eb61589 | |||
6d2a5b743b | |||
9c943d5a10 | |||
8293d44ee0 | |||
a3bd862e76 | |||
c424d5eac9 | |||
2e6f5173e0 | |||
ee4bba44ec | |||
8e923a4328 | |||
52efa04ee6 | |||
67d4965648 | |||
7a7b1cb305 | |||
377a9621ff | |||
432dd5a504 | |||
7446f5b1eb | |||
15ea999628 | |||
b5367ea3aa | |||
e022c02cb6 | |||
a13455d5b0 | |||
16741409e7 | |||
d7e8f8b3fa | |||
8c97c8f141 | |||
6a8755a13d | |||
4ed6d275a3 | |||
cb49eb8646 | |||
a7458dabf2 | |||
5856b7d873 | |||
7733824c21 | |||
f1d261044b | |||
95820431ab | |||
0002ecbdb2 | |||
2eb51b5270 | |||
1847f79571 | |||
58ff12f3f8 | |||
b0b7b4bd15 | |||
f3f1f36099 | |||
f8cfb1b90f | |||
393e1d6de2 | |||
a11556433b | |||
3ee9c05db7 | |||
de7ba2db6b | |||
8393454158 | |||
6b93ac7663 | |||
fe2410e9d8 | |||
d8ecf1c439 | |||
8577f6bd4d | |||
470be06d16 | |||
c6e4dae79b | |||
23f83eddd1 | |||
dd794fd004 | |||
f234433e33 | |||
d52773543d | |||
bd4ab0b530 | |||
6b9cd11be3 | |||
1443bd1e80 | |||
3fd8081dc5 | |||
dda949a6a4 | |||
128f0a2941 | |||
5f8a57398a | |||
327d64611b | |||
5b4cdce610 | |||
56e08e82d4 | |||
2df027c9f6 | |||
913af84931 | |||
de6064f9fd | |||
867c55a409 | |||
36420be2ce | |||
16e9dda64a | |||
1ef9a896bf | |||
75f2b84ded | |||
2268baf1ba | |||
9e43d989ea | |||
869725baee | |||
ae26ac3cd3 | |||
826af77396 | |||
294d03e18b | |||
b27116aedd | |||
074c0584f5 | |||
210e00703a | |||
3ffbddbdd8 | |||
5f827b5c7c | |||
a161e6dba9 | |||
7e70683d9b | |||
38bd94c078 | |||
83366498d3 | |||
929e089e4d | |||
a6b56b19d2 | |||
c2b6fdc198 | |||
43a4f046f0 | |||
b79bc2591c | |||
61198fd168 | |||
c1dd3835fe | |||
f3c9e87653 | |||
0064116c34 | |||
298fe18f29 | |||
6c539ab2a4 | |||
b6c59a7a0a | |||
f0536cedde | |||
48fa4ba962 | |||
39ff99b65e | |||
78c7b3b949 | |||
6a5dc1c508 | |||
9f84ec9bc9 | |||
e48b7db56f | |||
bffd54727c | |||
ab90f5f6e0 | |||
632415c746 | |||
0882c63eb1 | |||
ee6716c60e | |||
bb5ad8a596 | |||
951bc8509d | |||
ab8cd21633 | |||
721cd2f7ae | |||
13cd1da631 | |||
275fa2d125 | |||
4a8e018599 | |||
c1b5d44947 | |||
ae9db9fae8 | |||
104fbd19cd | |||
3b2571fced | |||
486c64798b | |||
3fb17c5f22 | |||
3f42e3d986 | |||
0c74debbb3 | |||
1854ee0ba1 | |||
99d9d7e2b5 | |||
e286435e19 | |||
52f9e2810f | |||
db60d7ac5f | |||
b17dccac3b | |||
b9bb0b1aeb | |||
a6f2edbbae | |||
3fcedbf13b | |||
274edaae2e | |||
8ed865d300 | |||
55db5c5ba3 | |||
b4f696484f | |||
12935d2cab | |||
a0cff79878 | |||
7a3c5937f2 | |||
f430720d2f | |||
34010986f7 | |||
d8a8d16bfc | |||
243f53336c | |||
d188be60c8 | |||
f1541a8cee | |||
cd1c4c768e | |||
967b6cc695 | |||
906c0861c7 | |||
4e0bb5c07d | |||
b57dd3e668 | |||
b2897ca159 | |||
041f9d7409 | |||
79282dac10 | |||
8142c6efc4 | |||
5f8b0edcdd | |||
326dd6c6dd | |||
4e4be7049e | |||
008ac9c516 | |||
43f9548483 | |||
27b7591770 | |||
acbd4035db | |||
cd98a68628 | |||
038957b937 | |||
d36945b9af | |||
c7acbf4481 | |||
1ec8a03636 | |||
a42ecc0cd9 | |||
0a2ac0e7e9 | |||
3e1c696d80 | |||
008b435921 | |||
5b3a53f48e | |||
eac217a85a | |||
e0bf6ce8af | |||
19c61f4726 | |||
3bc443ad99 | |||
308235936d | |||
1226a8dfc2 | |||
86c1d4c9ee |
137
.dockerignore
Normal file
137
.dockerignore
Normal file
@ -0,0 +1,137 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
.git
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.nupkg
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# vim swap files
|
||||
*swp
|
||||
*swo
|
||||
*~
|
||||
|
||||
# Ping data files
|
||||
*.ping
|
||||
*.pings
|
||||
*.population*
|
||||
*.percent
|
||||
*.cities
|
||||
populations
|
||||
|
||||
# Discarded code snippets
|
||||
build.sh
|
||||
*-fast.yaml
|
||||
detritus/
|
||||
|
||||
# Dotnet Core ignores
|
||||
*.swp
|
||||
*.pdb
|
||||
*.deps.json
|
||||
*.*~
|
||||
project.lock.json
|
||||
.DS_Store
|
||||
*.pyc
|
||||
nupkg/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
msbuild.log
|
||||
msbuild.err
|
||||
msbuild.wrn
|
||||
csharp/OpenMatch/obj
|
||||
Chart.lock
|
||||
|
||||
|
||||
# Visual Studio 2015
|
||||
.vs/
|
||||
|
||||
# Goland
|
||||
.idea/
|
||||
|
||||
# Nodejs files placed when building Hugo, ok to allow if we actually start using Nodejs.
|
||||
package.json
|
||||
package-lock.json
|
||||
|
||||
site/resources/_gen/
|
||||
|
||||
# Node Modules
|
||||
node_modules/
|
||||
|
||||
# Install YAML files, Helm is the source of truth for configuration.
|
||||
install/yaml/
|
||||
|
||||
# Temp Directories
|
||||
tmp/
|
||||
|
||||
# Terraform context
|
||||
.terraform
|
||||
*.tfstate
|
||||
*.tfstate.backup
|
||||
|
||||
# Credential Files
|
||||
creds.json
|
||||
|
||||
# Open Match Binaries
|
||||
cmd/backend/backend
|
||||
cmd/frontend/frontend
|
||||
cmd/query/query
|
||||
cmd/synchronizer/synchronizer
|
||||
cmd/minimatch/minimatch
|
||||
cmd/swaggerui/swaggerui
|
||||
tools/certgen/certgen
|
||||
examples/demo/demo
|
||||
examples/functions/golang/soloduel/soloduel
|
||||
test/evaluator/evaluator
|
||||
test/matchfunction/matchfunction
|
||||
tools/reaper/reaper
|
||||
|
||||
# Open Match Build Directory
|
||||
build/
|
||||
|
||||
# Secrets Directories
|
||||
install/helm/open-match/secrets/
|
||||
|
||||
# Helm tar charts
|
||||
install/helm/open-match/charts/
|
29
.gcloudignore
Normal file
29
.gcloudignore
Normal file
@ -0,0 +1,29 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This file specifies files that are *not* uploaded to Google Cloud Platform
|
||||
# using gcloud. It follows the same syntax as .gitignore, with the addition of
|
||||
# "#!include" directives (which insert the entries of the given .gitignore-style
|
||||
# file at that point).
|
||||
#
|
||||
# For more information, run:
|
||||
# $ gcloud topic gcloudignore
|
||||
#
|
||||
.gcloudignore
|
||||
# If you would like to upload your .git directory, .gitignore file or files
|
||||
# from your .gitignore file, remove the corresponding line
|
||||
# below:
|
||||
.git
|
||||
.gitignore
|
||||
#!include:.gitignore
|
25
.github/ISSUE_TEMPLATE/apichange.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE/apichange.md
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
name: Breaking API change
|
||||
about: Details of a breaking API change proposal.
|
||||
title: 'API change: <>'
|
||||
labels: breaking api change
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
<High level description of this change>
|
||||
|
||||
## Motivation
|
||||
|
||||
<What is the primary motivation for this API change>
|
||||
|
||||
## Impact
|
||||
|
||||
<What usage does this impact? Add details here such that a consumer of Open
|
||||
Match API can clearly tell if this will impact them>
|
||||
|
||||
## Change Proto
|
||||
|
||||
<Add snippet of the proposed change proto>
|
||||
|
30
.github/ISSUE_TEMPLATE/bugreport.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE/bugreport.md
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: kind/bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks!
|
||||
|
||||
If the matter is security related, please disclose it privately via
|
||||
-->
|
||||
|
||||
|
||||
**What happened**:
|
||||
|
||||
**What you expected to happen**:
|
||||
|
||||
**How to reproduce it (as minimally and precisely as possible)**:
|
||||
|
||||
**Anything else we need to know?**:
|
||||
|
||||
**Output of `kubectl version`**:
|
||||
|
||||
**Cloud Provider/Platform (AKS, GKE, Minikube etc.)**:
|
||||
|
||||
**Open Match Release Version**:
|
||||
|
||||
**Install Method(yaml/helm):**:
|
20
.github/ISSUE_TEMPLATE/featurerequest.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/featurerequest.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: kind/feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
181
.github/ISSUE_TEMPLATE/release.md
vendored
Normal file
181
.github/ISSUE_TEMPLATE/release.md
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
---
|
||||
name: Publish a Release
|
||||
about: Instructions and checklist for creating a release.
|
||||
title: 'Release X.Y.Z-rc.N'
|
||||
labels: kind/release
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Open Match Release Process
|
||||
|
||||
Follow these instructions to create an Open Match release. The output of the
|
||||
release process is new images and new configuration.
|
||||
|
||||
## Getting setup
|
||||
|
||||
**NOTE: The instructions below are NOT strictly copy-pastable and assume 0.5**
|
||||
**release. Please update the version number for your commands.**
|
||||
|
||||
The Git flow for pushing a new release is similar to the development process
|
||||
but there are some small differences.
|
||||
|
||||
### 1. Clone Repository
|
||||
|
||||
```shell
|
||||
# Clone your fork of the Open Match repository.
|
||||
git clone git@github.com:afeddersen/open-match.git
|
||||
# Change directory to the git repository.
|
||||
cd open-match
|
||||
# Add a remote, you'll be pushing to this.
|
||||
git remote add upstream https://github.com/googleforgames/open-match.git
|
||||
```
|
||||
|
||||
### 2. Release Branch
|
||||
|
||||
If you're creating the first release of the version, that would be `0.5.0-rc.1`
|
||||
then you'll need to create the release branch.
|
||||
|
||||
```shell
|
||||
# Create a local release branch.
|
||||
git checkout -b release-0.5 upstream/master
|
||||
# Push the branch upstream.
|
||||
git push upstream release-0.5
|
||||
```
|
||||
|
||||
otherwise there should already be a `release-0.5` branch so run,
|
||||
|
||||
```shell
|
||||
# Checkout the release branch.
|
||||
git checkout -b release-0.5 upstream/release-0.5
|
||||
```
|
||||
|
||||
**NOTE: The branch name must be in the format, `release-X.Y` otherwise**
|
||||
**some artifacts will not be pushed.**
|
||||
|
||||
## Releases & Versions
|
||||
|
||||
Open Match uses Semantic Versioning 2.0.0. If you're not familiar please
|
||||
see the documentation - [https://semver.org/](https://semver.org/).
|
||||
|
||||
Full Release / Stable Release:
|
||||
|
||||
* The final software product. Stable, reliable, etc...
|
||||
* Example: 1.0.0, 1.1.0
|
||||
|
||||
Release Candidate (RC):
|
||||
|
||||
* A release candidate (RC) is a version with the potential to be the final
|
||||
product but it hasn't validated by automated and/or manual tests.
|
||||
* Example: 1.0.0-rc.1
|
||||
|
||||
Hot Fixes:
|
||||
|
||||
* Code developed to correct a major software bug or fault
|
||||
that's been discovered after the full release.
|
||||
* Example: 1.0.1
|
||||
|
||||
Preview:
|
||||
|
||||
* Rare, a one off release cut from the master branch to provide early access
|
||||
to APIs or some other major change.
|
||||
* **NOTE: There's no branch for this release.**
|
||||
* Example: 0.5-preview.1
|
||||
|
||||
**NOTE: Semantic versioning is enforced by `go mod`. A non-compliant version**
|
||||
**tag will cause `go get` to break for users.**
|
||||
|
||||
# Detailed Instructions
|
||||
|
||||
## Find and replace
|
||||
|
||||
Below this point you will see {version} used as a placeholder for future
|
||||
releases. Find {version} and replace with the current release (e.g. 0.5.0)
|
||||
|
||||
## Create a release branch in the upstream open-match repository
|
||||
|
||||
**Note: This step is performed by the person who starts the release. It is
|
||||
only required once.**
|
||||
|
||||
- [ ] Create the branch in the **upstream** repository. It should be named
|
||||
release-X.Y. Example: release-0.5. At this point there's effectively a code
|
||||
freeze for this version and all work on master will be included in a future
|
||||
version. If you're on the branch that you created in the *getting setup*
|
||||
section above you should be able to push upstream.
|
||||
|
||||
```shell
|
||||
git push origin release-0.5
|
||||
```
|
||||
|
||||
- [ ] Announce a PR freeze on release-X.Y branch on [open-match-discuss@](mailing-list-post).
|
||||
- [ ] Open the [`Makefile`](makefile-version) and change BASE_VERSION entry.
|
||||
- [ ] Open the [`install/helm/open-match/Chart.yaml`](om-chart-yaml-version) and change the `appVersion` and `version` entries.
|
||||
- [ ] Open the [`install/helm/open-match/values.yaml`](om-values-yaml-version) and change the `tag` entries.
|
||||
- [ ] Open the [`cloudbuild.yaml`] and change the `_OM_VERSION` entry.
|
||||
- [ ] There might be additional references to the old version but be careful not to change it for places that have it for historical purposes.
|
||||
- [ ] Run `make release`
|
||||
- [ ] Run `make api/api.md` in open-match repo to update the auto-generated API references in open-match-docs repo.
|
||||
- [ ] Use the files under the `build/release/` directory for the Open Match installation guide. Make sure the artifacts work as expected - these are the artifacts that will be published to the GCS bucket and used in our release assets.
|
||||
- [ ] Create a PR with the changes, include the release candidate name, and point it to the release branch.
|
||||
- [ ] Go to [open-match-build](https://pantheon.corp.google.com/cloud-build/triggers?project=open-match-build) and update all *post submit* triggers' `_GCB_LATEST_VERSION` value to the `X.Y` of the release. This value should only increase as it's used to determine the latest stable version.
|
||||
- [ ] Merge your changes once the PR is approved.
|
||||
|
||||
## Create a release branch in the upstream open-match-docs repository
|
||||
- [ ] Open [`Makefile`](makefile-version) and change BASE_VERSION entry.
|
||||
- [ ] Open [`cloudbuild.yaml`] and change the `_OM_VERSION` entry.
|
||||
- [ ] Open [`site/config.toml`] and change the `release_version` entry.
|
||||
- [ ] Open [`site/static/swaggerui/config.json`] and change the `api/VERSION/...` entries
|
||||
- [ ] Create a PR with the changes, include the release candidate name, and point it to the release branch.
|
||||
|
||||
## Complete Milestone
|
||||
|
||||
**Note: This step is performed by the person who starts the release. It is
|
||||
only required once.**
|
||||
- [ ] Create the next [version milestone](https://github.com/googleforgames/open-match/milestones) and use [semantic versioning](https://semver.org/) when naming it to be consistent with the [Go community](https://blog.golang.org/versioning-proposal).
|
||||
- [ ] Create a *draft* [release](https://github.com/googleforgames/open-match/releases). Note that github has both "Pre-release" and "draft" as different concepts for a release. Until the release is finalized, only use "Save draft", and do not use "Publish release".
|
||||
- [ ] Use the [release template](https://github.com/googleforgames/open-match/blob/master/docs/governance/templates/release.md)
|
||||
- [ ] `Tag` = v{version}. Example: v0.5.0. Append -rc.# for release candidates. Example: v0.5.0-rc.1.
|
||||
- [ ] `Target` = release-X.Y. Example: release-0.5.
|
||||
- [ ] `Release Title` = `Tag`
|
||||
- [ ] `Write` section will contain the contents from the [release template](https://github.com/googleforgames/open-match/blob/master/docs/governance/templates/release.md).
|
||||
- [ ] Add the milestone to all PRs and issues that were merged since the last milestone. Look at the [releases page](https://github.com/googleforgames/open-match/releases) and look for the "X commits to master since this release" for the diff.
|
||||
- [ ] Review all [milestone-less closed issues](https://github.com/googleforgames/open-match/issues?q=is%3Aissue+is%3Aclosed+no%3Amilestone) and assign the appropriate milestone.
|
||||
- [ ] Review all [issues in milestone](https://github.com/googleforgames/open-match/milestones) for proper [labels](https://github.com/googleforgames/open-match/labels) (ex: area/build).
|
||||
- [ ] Review all [milestone-less closed PRs](https://github.com/googleforgames/open-match/pulls?q=is%3Apr+is%3Aclosed+no%3Amilestone) and assign the appropriate milestone.
|
||||
- [ ] Review all [PRs in milestone](https://github.com/googleforgames/open-match/milestones) for proper [labels](https://github.com/googleforgames/open-match/labels) (ex: area/build).
|
||||
- [ ] View all open entries in milestone and move them to a future milestone if they aren't getting closed in time. https://github.com/googleforgames/open-match/milestones/v{version}
|
||||
- [ ] Review all closed PRs against the milestone. Put the user visible changes into the release notes using the suggested format. https://github.com/googleforgames/open-match/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aclosed+is%3Amerged+milestone%3Av{version}
|
||||
- [ ] Review all closed issues against the milestone. Put the user visible changes into the release notes using the suggested format. https://github.com/googleforgames/open-match/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aclosed+milestone%3Av{version}
|
||||
- [ ] Verify the [milestone](https://github.com/googleforgames/open-match/milestones) is effectively 100% at this point with the exception of the release issue itself.
|
||||
|
||||
## Build Artifacts
|
||||
|
||||
- [ ] Go to the History section and find the "Post Submit" build of the merged commit that's running. Wait for it to go Green. If it's red, fix error repeat this section. Take note of the docker image version tag for next step. Example: 0.5.0-a4706cb.
|
||||
- [ ] Run `./docs/governance/templates/release.sh {source version tag} {version}` to copy the images to open-match-public-images.
|
||||
- [ ] If this is a new minor version in the newest major version then run `./docs/governance/templates/release.sh {source version tag} latest`.
|
||||
- [ ] Copy the files from `build/release/` generated from `make release` to the release draft you created. You can drag and drop the files using the Github UI.
|
||||
- [ ] Update [Slack invitation link](https://slack.com/help/articles/201330256-invite-new-members-to-your-workspace#share-an-invite-link) in [open-match.dev](https://open-match.dev/site/docs/contribute/#get-involved).
|
||||
- [ ] Test Open Match installation under GKE and Minikube enviroment using YAML files and Helm. Follow the [First Match](https://development.open-match.dev/site/docs/getting-started/first_match/) guide, run `make proxy-demo`, and open `localhost:51507` to make sure everything works.
|
||||
- [ ] Minikube: Run `make create-mini-cluster` to create a local cluster with latest Kubernetes API version.
|
||||
- [ ] GKE: Run `make create-gke-cluster` to create a GKE cluster.
|
||||
- [ ] Helm: Run `helm install open-match -n open-match open-match/open-match`
|
||||
- [ ] Update usage requirements in the Installation doc - e.g. supported minikube version, kubectl version, golang version, etc.
|
||||
|
||||
## Finalize
|
||||
|
||||
- [ ] Save the release as a draft.
|
||||
- [ ] Circulate the draft release to active contributors. Where reasonable, get everyone's ok on the release notes before continuing.
|
||||
- [ ] Publish the [Release](om-release) in Github. This will notify repository watchers.
|
||||
|
||||
## Announce
|
||||
|
||||
- [ ] Send an email to the [mailing list](mailing-list-post) with the release details (copy-paste the release blog post)
|
||||
- [ ] Send a chat on the [Slack channel](om-slack). "Open Match {version} has been released! Check it out at {release url}."
|
||||
|
||||
[om-slack]: https://open-match.slack.com/
|
||||
[mailing-list-post]: https://groups.google.com/forum/#!newtopic/open-match-discuss
|
||||
[release-template]: https://github.com/googleforgames/open-match/blob/master/docs/governance/templates/release.md
|
||||
[makefile-version]: https://github.com/googleforgames/open-match/blob/master/Makefile#L53
|
||||
[om-chart-yaml-version]: https://github.com/googleforgames/open-match/blob/master/install/helm/open-match/Chart.yaml#L16
|
||||
[om-values-yaml-version]: https://github.com/googleforgames/open-match/blob/master/install/helm/open-match/values.yaml#L16
|
||||
[om-release]: https://github.com/googleforgames/open-match/releases/new
|
||||
[readme-deploy]: https://github.com/googleforgames/open-match/blob/master/README.md#deploy-to-kubernetes
|
16
.github/pull_request_template.md
vendored
Normal file
16
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<!-- Thanks for sending a pull request! Here are some tips for you:
|
||||
If this is your first time, please read our contributor guidelines: https://github.com/googleforgames/open-match/blob/master/CONTRIBUTING.md and developer guide https://github.com/googleforgames/open-match/blob/master/docs/development.md
|
||||
-->
|
||||
|
||||
**What this PR does / Why we need it**:
|
||||
|
||||
**Which issue(s) this PR fixes**:
|
||||
<!--
|
||||
*Automatically closes linked issue when PR is merged.
|
||||
Usage: `Closes #<issue number>`, or `Closes (paste link of issue)`.
|
||||
-->
|
||||
Closes #
|
||||
|
||||
**Special notes for your reviewer**:
|
||||
|
||||
|
66
.gitignore
vendored
66
.gitignore
vendored
@ -1,7 +1,22 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.nupkg
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
@ -26,9 +41,13 @@ populations
|
||||
|
||||
# Discarded code snippets
|
||||
build.sh
|
||||
*-fast.yaml
|
||||
detritus/
|
||||
|
||||
# Dotnet Core ignores
|
||||
*.swp
|
||||
*.pdb
|
||||
*.deps.json
|
||||
*.*~
|
||||
project.lock.json
|
||||
.DS_Store
|
||||
@ -59,6 +78,53 @@ bld/
|
||||
msbuild.log
|
||||
msbuild.err
|
||||
msbuild.wrn
|
||||
csharp/OpenMatch/obj
|
||||
Chart.lock
|
||||
|
||||
# Visual Studio 2015
|
||||
.vs/
|
||||
|
||||
# Goland
|
||||
.idea/
|
||||
|
||||
# Nodejs files placed when building Hugo, ok to allow if we actually start using Nodejs.
|
||||
package.json
|
||||
package-lock.json
|
||||
|
||||
site/resources/_gen/
|
||||
|
||||
# Node Modules
|
||||
node_modules/
|
||||
|
||||
# Install YAML files
|
||||
install/yaml/
|
||||
|
||||
# Temp Directories
|
||||
tmp/
|
||||
|
||||
# Terraform context
|
||||
.terraform
|
||||
*.tfstate.backup
|
||||
|
||||
# Credential Files
|
||||
creds.json
|
||||
|
||||
# Open Match Binaries
|
||||
cmd/backend/backend
|
||||
cmd/frontend/frontend
|
||||
cmd/query/query
|
||||
cmd/synchronizer/synchronizer
|
||||
cmd/minimatch/minimatch
|
||||
cmd/swaggerui/swaggerui
|
||||
tools/certgen/certgen
|
||||
examples/demo/demo
|
||||
examples/functions/golang/soloduel/soloduel
|
||||
test/evaluator/evaluator
|
||||
test/matchfunction/matchfunction
|
||||
tools/reaper/reaper
|
||||
|
||||
# Secrets Directories
|
||||
install/helm/open-match/secrets/
|
||||
|
||||
# Helm tar charts
|
||||
install/helm/open-match/charts/
|
||||
|
225
.golangci.yaml
Normal file
225
.golangci.yaml
Normal file
@ -0,0 +1,225 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This file contains all available configuration options
|
||||
# with their default values.
|
||||
|
||||
# https://github.com/golangci/golangci-lint#config-file
|
||||
service:
|
||||
golangci-lint-version: 1.18.0
|
||||
|
||||
# options for analysis running
|
||||
run:
|
||||
# default concurrency is a available CPU number
|
||||
concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
deadline: 5m
|
||||
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
|
||||
# include test files or not, default is true
|
||||
tests: true
|
||||
|
||||
# list of build tags, all linters use it. Default is empty list.
|
||||
build-tags:
|
||||
|
||||
# which dirs to skip: they won't be analyzed;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but next dirs are always skipped independently
|
||||
# from this option's value:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs:
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
skip-files: '.*\.gw\.go'
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
|
||||
format: colored-line-number
|
||||
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
|
||||
# print linter name in the end of issue text, default is true
|
||||
print-linter-name: true
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
errcheck:
|
||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: true
|
||||
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: true
|
||||
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: true
|
||||
|
||||
# settings per analyzer
|
||||
settings:
|
||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||
funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
|
||||
golint:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
min-confidence: 0.8
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
simplify: true
|
||||
gocyclo:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 10
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
dupl:
|
||||
# tokens count to trigger issue, 150 by default
|
||||
threshold: 100
|
||||
goconst:
|
||||
# minimal length of string constant, 3 by default
|
||||
min-len: 3
|
||||
# minimal occurrences count to trigger, 3 by default
|
||||
min-occurrences: 3
|
||||
depguard:
|
||||
list-type: blacklist
|
||||
include-go-root: false
|
||||
packages:
|
||||
- github.com/davecgh/go-spew/spew
|
||||
misspell:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
locale: US
|
||||
ignore-words:
|
||||
- someword
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 120
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
||||
unused:
|
||||
# treat code as a program (not a library) and report unused exported identifiers; default is false.
|
||||
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
unparam:
|
||||
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
||||
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 30
|
||||
prealloc:
|
||||
# XXX: we don't recommend using this linter before doing performance profiling.
|
||||
# For most programs usage of prealloc will be a premature optimization.
|
||||
|
||||
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
|
||||
# True by default.
|
||||
simple: true
|
||||
range-loops: true # Report preallocation suggestions on range loops, true by default
|
||||
for-loops: false # Report preallocation suggestions on for loops, false by default
|
||||
gocritic:
|
||||
# Which checks should be enabled; can't be combined with 'disabled-checks';
|
||||
# See https://go-critic.github.io/overview#checks-overview
|
||||
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
|
||||
# By default list of stable checks is used.
|
||||
# enabled-checks:
|
||||
# - rangeValCopy
|
||||
|
||||
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks.
|
||||
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||
enabled-tags:
|
||||
- performance
|
||||
|
||||
settings: # settings passed to gocritic
|
||||
captLocal: # must be valid enabled check name
|
||||
paramsOnly: true
|
||||
rangeValCopy:
|
||||
sizeThreshold: 32
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- dupl
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- goconst
|
||||
- gocyclo
|
||||
- gosec
|
||||
- interfacer # deprecated - "A tool that suggests interfaces is prone to bad suggestions"
|
||||
- lll
|
||||
|
||||
#linters:
|
||||
# enable-all: true
|
||||
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
exclude:
|
||||
- abcdef
|
||||
|
||||
# Excluding configuration per-path, per-linter, per-text and per-source
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on test files
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- errcheck
|
||||
- bodyclose
|
||||
|
||||
# Exclude known linters from partially hard-vendored code,
|
||||
# which is impossible to exclude via "nolint" comments.
|
||||
- path: internal/hmac/
|
||||
text: "weak cryptographic primitive"
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
# Exclude some staticcheck messages
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "SA9003:"
|
||||
|
||||
# Exclude lll issues for long lines with go:generate
|
||||
- linters:
|
||||
- lll
|
||||
source: "^//go:generate "
|
||||
|
||||
# Independently from option `exclude` we use default exclude patterns,
|
||||
# it can be disabled by this option. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`.
|
||||
# Default value for this option is true.
|
||||
exclude-use-default: false
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-per-linter: 0
|
||||
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
max-same-issues: 0
|
@ -1,14 +0,0 @@
|
||||
# Golang application builder steps
|
||||
FROM golang:1.10.3 as builder
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match
|
||||
COPY cmd/backendapi cmd/backendapi
|
||||
COPY api/protobuf-spec/backend.pb.go cmd/backendapi/proto/
|
||||
COPY config config
|
||||
COPY internal internal
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match/cmd/backendapi
|
||||
RUN go get -d -v
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo .
|
||||
|
||||
#FROM scratch
|
||||
#COPY --from=builder /go/src/github.com/GoogleCloudPlatform/open-match/cmd/backendapi/backendapi .
|
||||
ENTRYPOINT ["./backendapi"]
|
28
Dockerfile.base-build
Normal file
28
Dockerfile.base-build
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# When updating Go version, update Dockerfile.ci, Dockerfile.base-build, and go.mod
|
||||
FROM golang:1.14.0
|
||||
ENV GO111MODULE=on
|
||||
|
||||
WORKDIR /go/src/open-match.dev/open-match
|
||||
|
||||
# First copy only the go.sum and go.mod then download dependencies. Docker
|
||||
# caching is [in]validated by the input files changes. So when the dependencies
|
||||
# for the project don't change, the previous image layer can be re-used. go.sum
|
||||
# is included as its hashing verifies the expected files are downloaded.
|
||||
COPY go.sum go.mod ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
57
Dockerfile.ci
Normal file
57
Dockerfile.ci
Normal file
@ -0,0 +1,57 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM debian
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y -qq git make python3 virtualenv curl sudo unzip apt-transport-https ca-certificates curl software-properties-common gnupg2
|
||||
|
||||
# Docker
|
||||
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
|
||||
RUN sudo apt-key fingerprint 0EBFCD88
|
||||
RUN sudo add-apt-repository \
|
||||
"deb [arch=amd64] https://download.docker.com/linux/debian \
|
||||
stretch \
|
||||
stable"
|
||||
RUN sudo apt-get update
|
||||
RUN sudo apt-get install -y -qq docker-ce docker-ce-cli containerd.io
|
||||
|
||||
# Cloud SDK
|
||||
RUN export CLOUD_SDK_REPO="cloud-sdk-stretch" && \
|
||||
echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \
|
||||
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
|
||||
apt-get update -y && apt-get install google-cloud-sdk google-cloud-sdk-app-engine-go -y -qq
|
||||
|
||||
# Install Golang
|
||||
# https://github.com/docker-library/golang/blob/master/1.14/stretch/Dockerfile
|
||||
RUN mkdir -p /toolchain/golang
|
||||
WORKDIR /toolchain/golang
|
||||
RUN sudo rm -rf /usr/local/go/
|
||||
|
||||
# When updating Go version, update Dockerfile.ci, Dockerfile.base-build, and go.mod
|
||||
RUN curl -L https://golang.org/dl/go1.14.linux-amd64.tar.gz | sudo tar -C /usr/local -xz
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
|
||||
|
||||
RUN sudo mkdir -p "$GOPATH/src" "$GOPATH/bin" \
|
||||
&& sudo chmod -R 777 "$GOPATH"
|
||||
|
||||
# Prepare toolchain and workspace
|
||||
RUN mkdir -p /toolchain
|
||||
|
||||
WORKDIR /workspace
|
||||
ENV OPEN_MATCH_CI_MODE=1
|
||||
ENV KUBECONFIG=$HOME/.kube/config
|
||||
RUN mkdir -p $HOME/.kube/
|
60
Dockerfile.cmd
Normal file
60
Dockerfile.cmd
Normal file
@ -0,0 +1,60 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM open-match-base-build as builder
|
||||
|
||||
WORKDIR /go/src/open-match.dev/open-match
|
||||
|
||||
ARG IMAGE_TITLE
|
||||
|
||||
RUN make "build/cmd/${IMAGE_TITLE}"
|
||||
|
||||
FROM gcr.io/distroless/static:nonroot
|
||||
ARG IMAGE_TITLE
|
||||
WORKDIR /app/
|
||||
|
||||
COPY --from=builder --chown=nonroot "/go/src/open-match.dev/open-match/build/cmd/${IMAGE_TITLE}/" "/app/"
|
||||
|
||||
ENTRYPOINT ["/app/run"]
|
||||
|
||||
# Docker Image Arguments
|
||||
ARG BUILD_DATE
|
||||
ARG VCS_REF
|
||||
ARG BUILD_VERSION
|
||||
|
||||
# Standardized Docker Image Labels
|
||||
# https://github.com/opencontainers/image-spec/blob/master/annotations.md
|
||||
LABEL \
|
||||
org.opencontainers.image.created="${BUILD_TIME}" \
|
||||
org.opencontainers.image.authors="Google LLC <open-match-discuss@googlegroups.com>" \
|
||||
org.opencontainers.image.url="https://open-match.dev/" \
|
||||
org.opencontainers.image.documentation="https://open-match.dev/site/docs/" \
|
||||
org.opencontainers.image.source="https://github.com/googleforgames/open-match" \
|
||||
org.opencontainers.image.version="${BUILD_VERSION}" \
|
||||
org.opencontainers.image.revision="1" \
|
||||
org.opencontainers.image.vendor="Google LLC" \
|
||||
org.opencontainers.image.licenses="Apache-2.0" \
|
||||
org.opencontainers.image.ref.name="" \
|
||||
org.opencontainers.image.title="${IMAGE_TITLE}" \
|
||||
org.opencontainers.image.description="Flexible, extensible, and scalable video game matchmaking." \
|
||||
org.label-schema.schema-version="1.0" \
|
||||
org.label-schema.build-date=$BUILD_DATE \
|
||||
org.label-schema.url="http://open-match.dev/" \
|
||||
org.label-schema.vcs-url="https://github.com/googleforgames/open-match" \
|
||||
org.label-schema.version=$BUILD_VERSION \
|
||||
org.label-schema.vcs-ref=$VCS_REF \
|
||||
org.label-schema.vendor="Google LLC" \
|
||||
org.label-schema.name="${IMAGE_TITLE}" \
|
||||
org.label-schema.description="Flexible, extensible, and scalable video game matchmaking." \
|
||||
org.label-schema.usage="https://open-match.dev/site/docs/"
|
@ -1,12 +0,0 @@
|
||||
# Golang application builder steps
|
||||
FROM golang:1.10.3 as builder
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match
|
||||
COPY examples/evaluators/golang/simple examples/evaluators/golang/simple
|
||||
COPY config config
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match/examples/evaluators/golang/simple
|
||||
RUN go get -d -v
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo .
|
||||
|
||||
#FROM scratch
|
||||
#COPY --from=builder /go/src/github.com/GoogleCloudPlatform/mmfstub/mmfstub mmfstub
|
||||
ENTRYPOINT ["./simple"]
|
@ -1,14 +0,0 @@
|
||||
# Golang application builder steps
|
||||
FROM golang:1.10.3 as builder
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match
|
||||
COPY cmd/frontendapi cmd/frontendapi
|
||||
COPY api/protobuf-spec/frontend.pb.go cmd/frontendapi/proto/
|
||||
COPY config config
|
||||
COPY internal internal
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match/cmd/frontendapi
|
||||
RUN go get -d -v
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo .
|
||||
|
||||
#FROM scratch
|
||||
#COPY --from=builder /go/src/github.com/GoogleCloudPlatform/open-match/cmd/frontendapi/frontendapi .
|
||||
ENTRYPOINT ["./frontendapi"]
|
@ -1,13 +0,0 @@
|
||||
# Golang application builder steps
|
||||
FROM golang:1.10.3 as builder
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match
|
||||
COPY examples/functions/golang/simple examples/functions/golang/simple
|
||||
COPY config config
|
||||
COPY internal/statestorage internal/statestorage
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match/examples/functions/golang/simple
|
||||
RUN go get -d -v
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o mmf .
|
||||
|
||||
#FROM scratch
|
||||
#COPY --from=builder /go/src/github.com/GoogleCloudPlatform/mmfstub/mmfstub mmfstub
|
||||
CMD ["./mmf"]
|
@ -1,24 +0,0 @@
|
||||
# Golang application builder steps
|
||||
FROM golang:1.10.3 as builder
|
||||
|
||||
# Necessary to get a specific version of the golang k8s client
|
||||
RUN go get github.com/tools/godep
|
||||
RUN go get k8s.io/client-go/...
|
||||
WORKDIR /go/src/k8s.io/client-go
|
||||
RUN git checkout v7.0.0
|
||||
RUN godep restore ./...
|
||||
RUN rm -rf vendor/
|
||||
RUN rm -rf /go/src/github.com/golang/protobuf/
|
||||
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match/
|
||||
COPY cmd/mmforc cmd/mmforc
|
||||
COPY config config
|
||||
COPY internal internal
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match/cmd/mmforc/
|
||||
RUN go get -d -v
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo .
|
||||
|
||||
# Uncomment to build production images (removes all troubleshooting tools)
|
||||
#FROM scratch
|
||||
#COPY --from=builder /go/src/github.com/GoogleCloudPlatform/open-match/cmd/mmforc/mmforc .
|
||||
CMD ["./mmforc"]
|
980
Makefile
Normal file
980
Makefile
Normal file
@ -0,0 +1,980 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
## Open Match Make Help
|
||||
## ====================
|
||||
##
|
||||
## Create a GKE Cluster (requires gcloud installed and initialized, https://cloud.google.com/sdk/docs/quickstarts)
|
||||
## make activate-gcp-apis
|
||||
## make create-gke-cluster push-helm
|
||||
##
|
||||
## Create a Minikube Cluster (requires VirtualBox)
|
||||
## make create-mini-cluster push-helm
|
||||
##
|
||||
## Create a KinD Cluster (Follow instructions to run command before pushing helm.)
|
||||
## make create-kind-cluster get-kind-kubeconfig
|
||||
## Finish KinD setup by installing helm:
|
||||
## make push-helm
|
||||
##
|
||||
## Deploy Open Match
|
||||
## make push-images -j$(nproc)
|
||||
## make install-chart
|
||||
##
|
||||
## Build and Test
|
||||
## make all -j$(nproc)
|
||||
## make test
|
||||
##
|
||||
## Access telemetry
|
||||
## make proxy-prometheus
|
||||
## make proxy-grafana
|
||||
## make proxy-ui
|
||||
##
|
||||
## Teardown
|
||||
## make delete-mini-cluster
|
||||
## make delete-gke-cluster
|
||||
## make delete-kind-cluster && export KUBECONFIG=""
|
||||
##
|
||||
## Prepare a Pull Request
|
||||
## make presubmit
|
||||
##
|
||||
|
||||
# If you want information on how to edit this file checkout,
|
||||
# http://makefiletutorial.com/
|
||||
|
||||
BASE_VERSION = 1.1.0
|
||||
SHORT_SHA = $(shell git rev-parse --short=7 HEAD | tr -d [:punct:])
|
||||
BRANCH_NAME = $(shell git rev-parse --abbrev-ref HEAD | tr -d [:punct:])
|
||||
VERSION = $(BASE_VERSION)-$(SHORT_SHA)
|
||||
BUILD_DATE = $(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
YEAR_MONTH = $(shell date -u +'%Y%m')
|
||||
YEAR_MONTH_DAY = $(shell date -u +'%Y%m%d')
|
||||
MAJOR_MINOR_VERSION = $(shell echo $(BASE_VERSION) | cut -d '.' -f1).$(shell echo $(BASE_VERSION) | cut -d '.' -f2)
|
||||
PROTOC_VERSION = 3.10.1
|
||||
HELM_VERSION = 3.0.0
|
||||
KUBECTL_VERSION = 1.16.2
|
||||
MINIKUBE_VERSION = latest
|
||||
GOLANGCI_VERSION = 1.18.0
|
||||
KIND_VERSION = 0.5.1
|
||||
SWAGGERUI_VERSION = 3.24.2
|
||||
GOOGLE_APIS_VERSION = aba342359b6743353195ca53f944fe71e6fb6cd4
|
||||
GRPC_GATEWAY_VERSION = 1.14.3
|
||||
TERRAFORM_VERSION = 0.12.13
|
||||
CHART_TESTING_VERSION = 2.4.0
|
||||
|
||||
# A workaround to simplify Open Match development workflow
|
||||
REDIS_DEV_PASSWORD = helloworld
|
||||
|
||||
ENABLE_SECURITY_HARDENING = 0
|
||||
GO = GO111MODULE=on go
|
||||
# Defines the absolute local directory of the open-match project
|
||||
REPOSITORY_ROOT := $(patsubst %/,%,$(dir $(abspath $(MAKEFILE_LIST))))
|
||||
BUILD_DIR = $(REPOSITORY_ROOT)/build
|
||||
TOOLCHAIN_DIR = $(BUILD_DIR)/toolchain
|
||||
TOOLCHAIN_BIN = $(TOOLCHAIN_DIR)/bin
|
||||
PROTOC_INCLUDES := $(REPOSITORY_ROOT)/third_party
|
||||
GCP_PROJECT_ID ?=
|
||||
GCP_PROJECT_FLAG = --project=$(GCP_PROJECT_ID)
|
||||
OPEN_MATCH_BUILD_PROJECT_ID = open-match-build
|
||||
OPEN_MATCH_PUBLIC_IMAGES_PROJECT_ID = open-match-public-images
|
||||
REGISTRY ?= gcr.io/$(GCP_PROJECT_ID)
|
||||
TAG = $(VERSION)
|
||||
ALTERNATE_TAG = dev
|
||||
VERSIONED_CANARY_TAG = $(BASE_VERSION)-canary
|
||||
DATED_CANARY_TAG = $(YEAR_MONTH_DAY)-canary
|
||||
CANARY_TAG = canary
|
||||
GKE_CLUSTER_NAME = om-cluster
|
||||
GCP_REGION = us-west1
|
||||
GCP_ZONE = us-west1-a
|
||||
GCP_LOCATION = $(GCP_ZONE)
|
||||
EXE_EXTENSION =
|
||||
GCP_LOCATION_FLAG = --zone $(GCP_ZONE)
|
||||
GO111MODULE = on
|
||||
GOLANG_TEST_COUNT = 1
|
||||
SWAGGERUI_PORT = 51500
|
||||
PROMETHEUS_PORT = 9090
|
||||
JAEGER_QUERY_PORT = 16686
|
||||
GRAFANA_PORT = 3000
|
||||
FRONTEND_PORT = 51504
|
||||
BACKEND_PORT = 51505
|
||||
QUERY_PORT = 51503
|
||||
SYNCHRONIZER_PORT = 51506
|
||||
DEMO_PORT = 51507
|
||||
PROTOC := $(TOOLCHAIN_BIN)/protoc$(EXE_EXTENSION)
|
||||
HELM = $(TOOLCHAIN_BIN)/helm$(EXE_EXTENSION)
|
||||
MINIKUBE = $(TOOLCHAIN_BIN)/minikube$(EXE_EXTENSION)
|
||||
KUBECTL = $(TOOLCHAIN_BIN)/kubectl$(EXE_EXTENSION)
|
||||
KIND = $(TOOLCHAIN_BIN)/kind$(EXE_EXTENSION)
|
||||
TERRAFORM = $(TOOLCHAIN_BIN)/terraform$(EXE_EXTENSION)
|
||||
CERTGEN = $(TOOLCHAIN_BIN)/certgen$(EXE_EXTENSION)
|
||||
GOLANGCI = $(TOOLCHAIN_BIN)/golangci-lint$(EXE_EXTENSION)
|
||||
CHART_TESTING = $(TOOLCHAIN_BIN)/ct$(EXE_EXTENSION)
|
||||
GCLOUD = gcloud --quiet
|
||||
OPEN_MATCH_HELM_NAME = open-match
|
||||
OPEN_MATCH_KUBERNETES_NAMESPACE = open-match
|
||||
OPEN_MATCH_SECRETS_DIR = $(REPOSITORY_ROOT)/install/helm/open-match/secrets
|
||||
GCLOUD_ACCOUNT_EMAIL = $(shell gcloud auth list --format yaml | grep ACTIVE -a2 | grep account: | cut -c 10-)
|
||||
_GCB_POST_SUBMIT ?= 0
|
||||
# Latest version triggers builds of :latest images.
|
||||
_GCB_LATEST_VERSION ?= undefined
|
||||
IMAGE_BUILD_ARGS = --build-arg BUILD_DATE=$(BUILD_DATE) --build-arg=VCS_REF=$(SHORT_SHA) --build-arg BUILD_VERSION=$(BASE_VERSION)
|
||||
GCLOUD_EXTRA_FLAGS =
|
||||
# Make port forwards accessible outside of the proxy machine.
|
||||
PORT_FORWARD_ADDRESS_FLAG = --address 0.0.0.0
|
||||
DASHBOARD_PORT = 9092
|
||||
|
||||
# Open Match Cluster E2E Test Variables
|
||||
OPEN_MATCH_CI_LABEL = open-match-ci
|
||||
|
||||
# This flag is set when running in Continuous Integration.
|
||||
ifdef OPEN_MATCH_CI_MODE
|
||||
export KUBECONFIG = $(HOME)/.kube/config
|
||||
GCLOUD = gcloud --quiet --no-user-output-enabled
|
||||
GKE_CLUSTER_NAME = open-match-ci
|
||||
endif
|
||||
|
||||
export PATH := $(TOOLCHAIN_BIN):$(PATH)
|
||||
|
||||
# Get the project from gcloud if it's not set.
|
||||
ifeq ($(GCP_PROJECT_ID),)
|
||||
export GCP_PROJECT_ID = $(shell gcloud config list --format 'value(core.project)')
|
||||
endif
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
HELM_PACKAGE = https://get.helm.sh/helm-v$(HELM_VERSION)-windows-amd64.zip
|
||||
MINIKUBE_PACKAGE = https://storage.googleapis.com/minikube/releases/$(MINIKUBE_VERSION)/minikube-windows-amd64.exe
|
||||
EXE_EXTENSION = .exe
|
||||
PROTOC_PACKAGE = https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-win64.zip
|
||||
KUBECTL_PACKAGE = https://storage.googleapis.com/kubernetes-release/release/v$(KUBECTL_VERSION)/bin/windows/amd64/kubectl.exe
|
||||
GOLANGCI_PACKAGE = https://github.com/golangci/golangci-lint/releases/download/v$(GOLANGCI_VERSION)/golangci-lint-$(GOLANGCI_VERSION)-windows-amd64.zip
|
||||
KIND_PACKAGE = https://github.com/kubernetes-sigs/kind/releases/download/v$(KIND_VERSION)/kind-windows-amd64
|
||||
TERRAFORM_PACKAGE = https://releases.hashicorp.com/terraform/$(TERRAFORM_VERSION)/terraform_$(TERRAFORM_VERSION)_windows_amd64.zip
|
||||
CHART_TESTING_PACKAGE = https://github.com/helm/chart-testing/releases/download/v$(CHART_TESTING_VERSION)/chart-testing_$(CHART_TESTING_VERSION)_windows_amd64.zip
|
||||
SED_REPLACE = sed -i
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
HELM_PACKAGE = https://get.helm.sh/helm-v$(HELM_VERSION)-linux-amd64.tar.gz
|
||||
MINIKUBE_PACKAGE = https://storage.googleapis.com/minikube/releases/$(MINIKUBE_VERSION)/minikube-linux-amd64
|
||||
PROTOC_PACKAGE = https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-linux-x86_64.zip
|
||||
KUBECTL_PACKAGE = https://storage.googleapis.com/kubernetes-release/release/v$(KUBECTL_VERSION)/bin/linux/amd64/kubectl
|
||||
GOLANGCI_PACKAGE = https://github.com/golangci/golangci-lint/releases/download/v$(GOLANGCI_VERSION)/golangci-lint-$(GOLANGCI_VERSION)-linux-amd64.tar.gz
|
||||
KIND_PACKAGE = https://github.com/kubernetes-sigs/kind/releases/download/v$(KIND_VERSION)/kind-linux-amd64
|
||||
TERRAFORM_PACKAGE = https://releases.hashicorp.com/terraform/$(TERRAFORM_VERSION)/terraform_$(TERRAFORM_VERSION)_linux_amd64.zip
|
||||
CHART_TESTING_PACKAGE = https://github.com/helm/chart-testing/releases/download/v$(CHART_TESTING_VERSION)/chart-testing_$(CHART_TESTING_VERSION)_linux_amd64.tar.gz
|
||||
SED_REPLACE = sed -i
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
HELM_PACKAGE = https://get.helm.sh/helm-v$(HELM_VERSION)-darwin-amd64.tar.gz
|
||||
MINIKUBE_PACKAGE = https://storage.googleapis.com/minikube/releases/$(MINIKUBE_VERSION)/minikube-darwin-amd64
|
||||
PROTOC_PACKAGE = https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-osx-x86_64.zip
|
||||
KUBECTL_PACKAGE = https://storage.googleapis.com/kubernetes-release/release/v$(KUBECTL_VERSION)/bin/darwin/amd64/kubectl
|
||||
GOLANGCI_PACKAGE = https://github.com/golangci/golangci-lint/releases/download/v$(GOLANGCI_VERSION)/golangci-lint-$(GOLANGCI_VERSION)-darwin-amd64.tar.gz
|
||||
KIND_PACKAGE = https://github.com/kubernetes-sigs/kind/releases/download/v$(KIND_VERSION)/kind-darwin-amd64
|
||||
TERRAFORM_PACKAGE = https://releases.hashicorp.com/terraform/$(TERRAFORM_VERSION)/terraform_$(TERRAFORM_VERSION)_darwin_amd64.zip
|
||||
CHART_TESTING_PACKAGE = https://github.com/helm/chart-testing/releases/download/v$(CHART_TESTING_VERSION)/chart-testing_$(CHART_TESTING_VERSION)_darwin_amd64.tar.gz
|
||||
SED_REPLACE = sed -i ''
|
||||
endif
|
||||
endif
|
||||
|
||||
GOLANG_PROTOS = pkg/pb/backend.pb.go pkg/pb/frontend.pb.go pkg/pb/matchfunction.pb.go pkg/pb/query.pb.go pkg/pb/messages.pb.go pkg/pb/extensions.pb.go pkg/pb/evaluator.pb.go internal/ipb/synchronizer.pb.go pkg/pb/backend.pb.gw.go pkg/pb/frontend.pb.gw.go pkg/pb/matchfunction.pb.gw.go pkg/pb/query.pb.gw.go pkg/pb/evaluator.pb.gw.go
|
||||
|
||||
SWAGGER_JSON_DOCS = api/frontend.swagger.json api/backend.swagger.json api/query.swagger.json api/matchfunction.swagger.json api/evaluator.swagger.json
|
||||
|
||||
ALL_PROTOS = $(GOLANG_PROTOS) $(SWAGGER_JSON_DOCS)
|
||||
|
||||
# CMDS is a list of all folders in cmd/
|
||||
CMDS = $(notdir $(wildcard cmd/*))
|
||||
|
||||
# Names of the individual images, ommiting the openmatch prefix.
|
||||
IMAGES = $(CMDS) mmf-go-soloduel base-build
|
||||
|
||||
help:
|
||||
@cat Makefile | grep ^\#\# | grep -v ^\#\#\# |cut -c 4-
|
||||
|
||||
local-cloud-build: LOCAL_CLOUD_BUILD_PUSH = # --push
|
||||
local-cloud-build: gcloud
|
||||
cloud-build-local --config=cloudbuild.yaml --dryrun=false $(LOCAL_CLOUD_BUILD_PUSH) --substitutions SHORT_SHA=$(SHORT_SHA),_GCB_POST_SUBMIT=$(_GCB_POST_SUBMIT),_GCB_LATEST_VERSION=$(_GCB_LATEST_VERSION),BRANCH_NAME=$(BRANCH_NAME) .
|
||||
|
||||
################################################################################
|
||||
## #############################################################################
|
||||
## Image commands:
|
||||
## These commands are auto-generated based on a complete list of images. All
|
||||
## folders in cmd/ are turned into an image using Dockerfile.cmd. Additional
|
||||
## images are specified by the IMAGES variable. Image commands ommit the
|
||||
## "openmatch-" prefix on the image name and tags.
|
||||
##
|
||||
|
||||
list-images:
|
||||
@echo $(IMAGES)
|
||||
|
||||
#######################################
|
||||
## build-images / build-<image name>-image: builds images locally
|
||||
##
|
||||
build-images: $(foreach IMAGE,$(IMAGES),build-$(IMAGE)-image)
|
||||
|
||||
# Include all-protos here so that all dependencies are guaranteed to be downloaded after the base image is created.
|
||||
# This is important so that the repository does not have any mutations while building individual images.
|
||||
build-base-build-image: docker $(ALL_PROTOS)
|
||||
docker build -f Dockerfile.base-build -t open-match-base-build -t $(REGISTRY)/openmatch-base-build:$(TAG) -t $(REGISTRY)/openmatch-base-build:$(ALTERNATE_TAG) .
|
||||
|
||||
$(foreach CMD,$(CMDS),build-$(CMD)-image): build-%-image: docker build-base-build-image
|
||||
docker build \
|
||||
-f Dockerfile.cmd \
|
||||
$(IMAGE_BUILD_ARGS) \
|
||||
--build-arg=IMAGE_TITLE=$* \
|
||||
-t $(REGISTRY)/openmatch-$*:$(TAG) \
|
||||
-t $(REGISTRY)/openmatch-$*:$(ALTERNATE_TAG) \
|
||||
.
|
||||
|
||||
build-mmf-go-soloduel-image: docker build-base-build-image
|
||||
docker build -f examples/functions/golang/soloduel/Dockerfile -t $(REGISTRY)/openmatch-mmf-go-soloduel:$(TAG) -t $(REGISTRY)/openmatch-mmf-go-soloduel:$(ALTERNATE_TAG) .
|
||||
|
||||
#######################################
|
||||
## push-images / push-<image name>-image: builds and pushes images to your
|
||||
## container registry.
|
||||
##
|
||||
push-images: $(foreach IMAGE,$(IMAGES),push-$(IMAGE)-image)
|
||||
|
||||
$(foreach IMAGE,$(IMAGES),push-$(IMAGE)-image): push-%-image: build-%-image docker
|
||||
docker push $(REGISTRY)/openmatch-$*:$(TAG)
|
||||
docker push $(REGISTRY)/openmatch-$*:$(ALTERNATE_TAG)
|
||||
ifeq ($(_GCB_POST_SUBMIT),1)
|
||||
docker tag $(REGISTRY)/openmatch-$*:$(TAG) $(REGISTRY)/openmatch-$*:$(VERSIONED_CANARY_TAG)
|
||||
docker push $(REGISTRY)/openmatch-$*:$(VERSIONED_CANARY_TAG)
|
||||
ifeq ($(BASE_VERSION),0.0.0-dev)
|
||||
docker tag $(REGISTRY)/openmatch-$*:$(TAG) $(REGISTRY)/openmatch-$*:$(DATED_CANARY_TAG)
|
||||
docker push $(REGISTRY)/openmatch-$*:$(DATED_CANARY_TAG)
|
||||
docker tag $(REGISTRY)/openmatch-$*:$(TAG) $(REGISTRY)/openmatch-$*:$(CANARY_TAG)
|
||||
docker push $(REGISTRY)/openmatch-$*:$(CANARY_TAG)
|
||||
endif
|
||||
endif
|
||||
|
||||
#######################################
|
||||
## retag-images / retag-<image name>-image: publishes images on the public
|
||||
## container registry. Used for publishing releases.
|
||||
##
|
||||
retag-images: $(foreach IMAGE,$(IMAGES),retag-$(IMAGE)-image)
|
||||
|
||||
retag-%-image: SOURCE_REGISTRY = gcr.io/$(OPEN_MATCH_BUILD_PROJECT_ID)
|
||||
retag-%-image: TARGET_REGISTRY = gcr.io/$(OPEN_MATCH_PUBLIC_IMAGES_PROJECT_ID)
|
||||
retag-%-image: SOURCE_TAG = canary
|
||||
$(foreach IMAGE,$(IMAGES),retag-$(IMAGE)-image): retag-%-image: docker
|
||||
docker pull $(SOURCE_REGISTRY)/openmatch-$*:$(SOURCE_TAG)
|
||||
docker tag $(SOURCE_REGISTRY)/openmatch-$*:$(SOURCE_TAG) $(TARGET_REGISTRY)/openmatch-$*:$(TAG)
|
||||
docker push $(TARGET_REGISTRY)/openmatch-$*:$(TAG)
|
||||
|
||||
#######################################
|
||||
## clean-images / clean-<image name>-image: removes images from local docker
|
||||
##
|
||||
clean-images: docker $(foreach IMAGE,$(IMAGES),clean-$(IMAGE)-image)
|
||||
-docker rmi -f open-match-base-build
|
||||
|
||||
$(foreach IMAGE,$(IMAGES),clean-$(IMAGE)-image): clean-%-image:
|
||||
-docker rmi -f $(REGISTRY)/openmatch-$*:$(TAG) $(REGISTRY)/openmatch-$*:$(ALTERNATE_TAG)
|
||||
|
||||
#####################################################################################################################
|
||||
update-chart-deps: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
(cd $(REPOSITORY_ROOT)/install/helm/open-match; $(HELM) repo add incubator https://charts.helm.sh/stable; $(HELM) dependency update)
|
||||
|
||||
lint-chart: build/toolchain/bin/helm$(EXE_EXTENSION) build/toolchain/bin/ct$(EXE_EXTENSION)
|
||||
(cd $(REPOSITORY_ROOT)/install/helm; $(HELM) lint $(OPEN_MATCH_HELM_NAME))
|
||||
$(CHART_TESTING) lint --all --chart-yaml-schema $(TOOLCHAIN_BIN)/etc/chart_schema.yaml --lint-conf $(TOOLCHAIN_BIN)/etc/lintconf.yaml --chart-dirs $(REPOSITORY_ROOT)/install/helm/
|
||||
$(CHART_TESTING) lint-and-install --all --chart-yaml-schema $(TOOLCHAIN_BIN)/etc/chart_schema.yaml --lint-conf $(TOOLCHAIN_BIN)/etc/lintconf.yaml --chart-dirs $(REPOSITORY_ROOT)/install/helm/
|
||||
|
||||
build/chart/open-match-$(BASE_VERSION).tgz: build/toolchain/bin/helm$(EXE_EXTENSION) lint-chart
|
||||
mkdir -p $(BUILD_DIR)/chart/
|
||||
$(HELM) package -d $(BUILD_DIR)/chart/ --version $(BASE_VERSION) $(REPOSITORY_ROOT)/install/helm/open-match
|
||||
|
||||
build/chart/index.yaml: build/toolchain/bin/helm$(EXE_EXTENSION) gcloud build/chart/open-match-$(BASE_VERSION).tgz
|
||||
mkdir -p $(BUILD_DIR)/chart-index/
|
||||
-gsutil cp gs://open-match-chart/chart/index.yaml $(BUILD_DIR)/chart-index/
|
||||
-gsutil -m cp gs://open-match-chart/chart/open-match-* $(BUILD_DIR)/chart-index/
|
||||
$(HELM) repo index $(BUILD_DIR)/chart-index/
|
||||
$(HELM) repo index --merge $(BUILD_DIR)/chart-index/index.yaml $(BUILD_DIR)/chart/
|
||||
|
||||
build/chart/index.yaml.$(YEAR_MONTH_DAY): build/chart/index.yaml
|
||||
cp $(BUILD_DIR)/chart/index.yaml $(BUILD_DIR)/chart/index.yaml.$(YEAR_MONTH_DAY)
|
||||
|
||||
build/chart/: build/chart/index.yaml build/chart/index.yaml.$(YEAR_MONTH_DAY)
|
||||
|
||||
install-chart-prerequisite: build/toolchain/bin/kubectl$(EXE_EXTENSION) update-chart-deps
|
||||
-$(KUBECTL) create namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE)
|
||||
$(KUBECTL) apply -f install/gke-metadata-server-workaround.yaml
|
||||
|
||||
# Used for Open Match development. Install om-configmap-override.yaml by default.
|
||||
HELM_UPGRADE_FLAGS = --cleanup-on-fail -i --no-hooks --debug --timeout=600s --namespace=$(OPEN_MATCH_KUBERNETES_NAMESPACE) --set global.gcpProjectId=$(GCP_PROJECT_ID) --set open-match-override.enabled=true --set redis.password=$(REDIS_DEV_PASSWORD)
|
||||
# Used for generate static yamls. Install om-configmap-override.yaml as needed.
|
||||
HELM_TEMPLATE_FLAGS = --no-hooks --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --set usingHelmTemplate=true
|
||||
HELM_IMAGE_FLAGS = --set global.image.registry=$(REGISTRY) --set global.image.tag=$(TAG)
|
||||
|
||||
install-demo: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
cp $(REPOSITORY_ROOT)/install/02-open-match-demo.yaml $(REPOSITORY_ROOT)/install/tmp-demo.yaml
|
||||
$(SED_REPLACE) 's|gcr.io/open-match-public-images|$(REGISTRY)|g' $(REPOSITORY_ROOT)/install/tmp-demo.yaml
|
||||
$(SED_REPLACE) 's|0.0.0-dev|$(TAG)|g' $(REPOSITORY_ROOT)/install/tmp-demo.yaml
|
||||
$(KUBECTL) apply -f $(REPOSITORY_ROOT)/install/tmp-demo.yaml
|
||||
rm $(REPOSITORY_ROOT)/install/tmp-demo.yaml
|
||||
|
||||
# install-large-chart will install open-match-core, open-match-demo with the demo evaluator and mmf, and telemetry supports.
|
||||
install-large-chart: install-chart-prerequisite install-demo build/toolchain/bin/helm$(EXE_EXTENSION) install/helm/open-match/secrets/
|
||||
$(HELM) upgrade $(OPEN_MATCH_HELM_NAME) $(HELM_UPGRADE_FLAGS) --atomic install/helm/open-match $(HELM_IMAGE_FLAGS) \
|
||||
--set open-match-telemetry.enabled=true \
|
||||
--set open-match-customize.enabled=true \
|
||||
--set open-match-customize.evaluator.enabled=true \
|
||||
--set global.telemetry.grafana.enabled=true \
|
||||
--set global.telemetry.jaeger.enabled=true \
|
||||
--set global.telemetry.prometheus.enabled=true
|
||||
|
||||
# install-chart will install open-match-core, open-match-demo, with the demo evaluator and mmf.
|
||||
install-chart: install-chart-prerequisite install-demo build/toolchain/bin/helm$(EXE_EXTENSION) install/helm/open-match/secrets/
|
||||
$(HELM) upgrade $(OPEN_MATCH_HELM_NAME) $(HELM_UPGRADE_FLAGS) --atomic install/helm/open-match $(HELM_IMAGE_FLAGS) \
|
||||
--set open-match-customize.enabled=true \
|
||||
--set open-match-customize.evaluator.enabled=true
|
||||
|
||||
# install-scale-chart will wait for installing open-match-core with telemetry supports then install open-match-scale chart.
|
||||
install-scale-chart: install-chart-prerequisite build/toolchain/bin/helm$(EXE_EXTENSION) install/helm/open-match/secrets/
|
||||
$(HELM) upgrade $(OPEN_MATCH_HELM_NAME) $(HELM_UPGRADE_FLAGS) --atomic install/helm/open-match $(HELM_IMAGE_FLAGS) -f install/helm/open-match/values-production.yaml \
|
||||
--set open-match-telemetry.enabled=true \
|
||||
--set open-match-customize.enabled=true \
|
||||
--set open-match-customize.function.enabled=true \
|
||||
--set open-match-customize.evaluator.enabled=true \
|
||||
--set open-match-customize.function.image=openmatch-scale-mmf \
|
||||
--set global.telemetry.grafana.enabled=true \
|
||||
--set global.telemetry.jaeger.enabled=false \
|
||||
--set global.telemetry.prometheus.enabled=true
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME)-scale install/helm/open-match $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) -f install/helm/open-match/values-production.yaml \
|
||||
--set open-match-core.enabled=false \
|
||||
--set open-match-core.redis.enabled=false \
|
||||
--set global.telemetry.prometheus.enabled=true \
|
||||
--set global.telemetry.grafana.enabled=true \
|
||||
--set open-match-scale.enabled=true | $(KUBECTL) apply -f -
|
||||
|
||||
# install-ci-chart will install open-match-core with pool based mmf for end-to-end in-cluster test.
|
||||
install-ci-chart: install-chart-prerequisite build/toolchain/bin/helm$(EXE_EXTENSION) install/helm/open-match/secrets/
|
||||
$(HELM) upgrade $(OPEN_MATCH_HELM_NAME) $(HELM_UPGRADE_FLAGS) --atomic install/helm/open-match $(HELM_IMAGE_FLAGS) \
|
||||
--set query.replicas=1,frontend.replicas=1,backend.replicas=1 \
|
||||
--set evaluator.hostName=open-match-test \
|
||||
--set evaluator.grpcPort=50509 \
|
||||
--set evaluator.httpPort=51509 \
|
||||
--set open-match-core.registrationInterval=200ms \
|
||||
--set open-match-core.proposalCollectionInterval=200ms \
|
||||
--set open-match-core.assignedDeleteTimeout=200ms \
|
||||
--set open-match-core.pendingReleaseTimeout=200ms \
|
||||
--set open-match-core.queryPageSize=10 \
|
||||
--set global.gcpProjectId=intentionally-invalid-value \
|
||||
--set redis.master.resources.requests.cpu=0.6,redis.master.resources.requests.memory=300Mi \
|
||||
--set ci=true
|
||||
|
||||
delete-chart: build/toolchain/bin/helm$(EXE_EXTENSION) build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
-$(HELM) uninstall $(OPEN_MATCH_HELM_NAME)
|
||||
-$(HELM) uninstall $(OPEN_MATCH_HELM_NAME)-demo
|
||||
-$(KUBECTL) delete psp,clusterrole,clusterrolebinding --selector=release=open-match
|
||||
-$(KUBECTL) delete psp,clusterrole,clusterrolebinding --selector=release=open-match-demo
|
||||
-$(KUBECTL) delete namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE)
|
||||
-$(KUBECTL) delete namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE)-demo
|
||||
|
||||
ifneq ($(BASE_VERSION), 0.0.0-dev)
|
||||
install/yaml/: REGISTRY = gcr.io/$(OPEN_MATCH_PUBLIC_IMAGES_PROJECT_ID)
|
||||
install/yaml/: TAG = $(BASE_VERSION)
|
||||
endif
|
||||
install/yaml/: update-chart-deps install/yaml/install.yaml install/yaml/01-open-match-core.yaml install/yaml/02-open-match-demo.yaml install/yaml/03-prometheus-chart.yaml install/yaml/04-grafana-chart.yaml install/yaml/05-jaeger-chart.yaml install/yaml/06-open-match-override-configmap.yaml install/yaml/07-open-match-default-evaluator.yaml
|
||||
|
||||
# We have to hard-code the Jaeger endpoints as we are excluding Jaeger, so Helm cannot determine the endpoints from the Jaeger subchart
|
||||
install/yaml/01-open-match-core.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME) $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) \
|
||||
--set-string global.telemetry.jaeger.agentEndpoint="$(OPEN_MATCH_HELM_NAME)-jaeger-agent:6831" \
|
||||
--set-string global.telemetry.jaeger.collectorEndpoint="http://$(OPEN_MATCH_HELM_NAME)-jaeger-collector:14268/api/traces" \
|
||||
install/helm/open-match > install/yaml/01-open-match-core.yaml
|
||||
|
||||
install/yaml/02-open-match-demo.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
cp $(REPOSITORY_ROOT)/install/02-open-match-demo.yaml $(REPOSITORY_ROOT)/install/yaml/02-open-match-demo.yaml
|
||||
$(SED_REPLACE) 's|0.0.0-dev|$(TAG)|g' $(REPOSITORY_ROOT)/install/yaml/02-open-match-demo.yaml
|
||||
$(SED_REPLACE) 's|gcr.io/open-match-public-images|$(REGISTRY)|g' $(REPOSITORY_ROOT)/install/yaml/02-open-match-demo.yaml
|
||||
|
||||
install/yaml/03-prometheus-chart.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME) $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) \
|
||||
--set open-match-core.enabled=false \
|
||||
--set open-match-core.redis.enabled=false \
|
||||
--set open-match-telemetry.enabled=true \
|
||||
--set global.telemetry.prometheus.enabled=true \
|
||||
install/helm/open-match > install/yaml/03-prometheus-chart.yaml
|
||||
|
||||
# We have to hard-code the Prometheus Server URL as we are excluding Prometheus, so Helm cannot determine the URL from the Prometheus subchart
|
||||
install/yaml/04-grafana-chart.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME) $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) \
|
||||
--set open-match-core.enabled=false \
|
||||
--set open-match-core.redis.enabled=false \
|
||||
--set open-match-telemetry.enabled=true \
|
||||
--set global.telemetry.grafana.enabled=true \
|
||||
--set-string global.telemetry.grafana.prometheusServer="http://$(OPEN_MATCH_HELM_NAME)-prometheus-server.$(OPEN_MATCH_KUBERNETES_NAMESPACE).svc.cluster.local:80/" \
|
||||
install/helm/open-match > install/yaml/04-grafana-chart.yaml
|
||||
|
||||
install/yaml/05-jaeger-chart.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME) $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) \
|
||||
--set open-match-core.enabled=false \
|
||||
--set open-match-core.redis.enabled=false \
|
||||
--set open-match-telemetry.enabled=true \
|
||||
--set global.telemetry.jaeger.enabled=true \
|
||||
install/helm/open-match > install/yaml/05-jaeger-chart.yaml
|
||||
|
||||
install/yaml/06-open-match-override-configmap.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME) $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) \
|
||||
--set open-match-core.enabled=false \
|
||||
--set open-match-core.redis.enabled=false \
|
||||
--set open-match-override.enabled=true \
|
||||
-s templates/om-configmap-override.yaml \
|
||||
install/helm/open-match > install/yaml/06-open-match-override-configmap.yaml
|
||||
|
||||
install/yaml/07-open-match-default-evaluator.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME) $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) \
|
||||
--set open-match-core.enabled=false \
|
||||
--set open-match-core.redis.enabled=false \
|
||||
--set open-match-customize.enabled=true \
|
||||
--set open-match-customize.evaluator.enabled=true \
|
||||
install/helm/open-match > install/yaml/07-open-match-default-evaluator.yaml
|
||||
|
||||
install/yaml/install.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME) $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) \
|
||||
--set open-match-customize.enabled=true \
|
||||
--set open-match-customize.evaluator.enabled=true \
|
||||
--set open-match-telemetry.enabled=true \
|
||||
--set global.telemetry.jaeger.enabled=true \
|
||||
--set global.telemetry.grafana.enabled=true \
|
||||
--set global.telemetry.prometheus.enabled=true \
|
||||
install/helm/open-match > install/yaml/install.yaml
|
||||
|
||||
set-redis-password:
|
||||
@stty -echo; \
|
||||
printf "Redis password: "; \
|
||||
read REDIS_PASSWORD; \
|
||||
stty echo; \
|
||||
printf "\n"; \
|
||||
$(KUBECTL) create secret generic open-match-redis -n $(OPEN_MATCH_KUBERNETES_NAMESPACE) --from-literal=redis-password=$$REDIS_PASSWORD --dry-run -o yaml | $(KUBECTL) replace -f - --force
|
||||
|
||||
install-toolchain: install-kubernetes-tools install-protoc-tools install-openmatch-tools
|
||||
install-kubernetes-tools: build/toolchain/bin/kubectl$(EXE_EXTENSION) build/toolchain/bin/helm$(EXE_EXTENSION) build/toolchain/bin/minikube$(EXE_EXTENSION) build/toolchain/bin/terraform$(EXE_EXTENSION)
|
||||
install-protoc-tools: build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-go$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-grpc-gateway$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-swagger$(EXE_EXTENSION)
|
||||
install-openmatch-tools: build/toolchain/bin/certgen$(EXE_EXTENSION) build/toolchain/bin/reaper$(EXE_EXTENSION)
|
||||
|
||||
build/toolchain/bin/helm$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
mkdir -p $(TOOLCHAIN_DIR)/temp-helm
|
||||
ifeq ($(suffix $(HELM_PACKAGE)),.zip)
|
||||
cd $(TOOLCHAIN_DIR)/temp-helm && curl -Lo helm.zip $(HELM_PACKAGE) && unzip -d $(TOOLCHAIN_BIN) -j -q -o helm.zip
|
||||
else
|
||||
cd $(TOOLCHAIN_DIR)/temp-helm && curl -Lo helm.tar.gz $(HELM_PACKAGE) && tar xzf helm.tar.gz -C $(TOOLCHAIN_BIN) --strip-components 1
|
||||
endif
|
||||
rm -rf $(TOOLCHAIN_DIR)/temp-helm/
|
||||
|
||||
build/toolchain/bin/ct$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
mkdir -p $(TOOLCHAIN_DIR)/temp-charttesting
|
||||
ifeq ($(suffix $(CHART_TESTING_PACKAGE)),.zip)
|
||||
cd $(TOOLCHAIN_DIR)/temp-charttesting && curl -Lo charttesting.zip $(CHART_TESTING_PACKAGE) && unzip -j -q -o charttesting.zip
|
||||
else
|
||||
cd $(TOOLCHAIN_DIR)/temp-charttesting && curl -Lo charttesting.tar.gz $(CHART_TESTING_PACKAGE) && tar xzf charttesting.tar.gz
|
||||
endif
|
||||
mv $(TOOLCHAIN_DIR)/temp-charttesting/* $(TOOLCHAIN_BIN)
|
||||
rm -rf $(TOOLCHAIN_DIR)/temp-charttesting/
|
||||
|
||||
build/toolchain/bin/minikube$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
curl -Lo $(MINIKUBE) $(MINIKUBE_PACKAGE)
|
||||
chmod +x $(MINIKUBE)
|
||||
|
||||
build/toolchain/bin/kubectl$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
curl -Lo $(KUBECTL) $(KUBECTL_PACKAGE)
|
||||
chmod +x $(KUBECTL)
|
||||
|
||||
build/toolchain/bin/golangci-lint$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
mkdir -p $(TOOLCHAIN_DIR)/temp-golangci
|
||||
ifeq ($(suffix $(GOLANGCI_PACKAGE)),.zip)
|
||||
cd $(TOOLCHAIN_DIR)/temp-golangci && curl -Lo golangci.zip $(GOLANGCI_PACKAGE) && unzip -j -q -o golangci.zip
|
||||
else
|
||||
cd $(TOOLCHAIN_DIR)/temp-golangci && curl -Lo golangci.tar.gz $(GOLANGCI_PACKAGE) && tar xzf golangci.tar.gz --strip-components 1
|
||||
endif
|
||||
mv $(TOOLCHAIN_DIR)/temp-golangci/golangci-lint$(EXE_EXTENSION) $(GOLANGCI)
|
||||
rm -rf $(TOOLCHAIN_DIR)/temp-golangci/
|
||||
|
||||
build/toolchain/bin/kind$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
curl -Lo $(KIND) $(KIND_PACKAGE)
|
||||
chmod +x $(KIND)
|
||||
|
||||
build/toolchain/bin/terraform$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
mkdir -p $(TOOLCHAIN_DIR)/temp-terraform
|
||||
cd $(TOOLCHAIN_DIR)/temp-terraform && curl -Lo terraform.zip $(TERRAFORM_PACKAGE) && unzip -j -q -o terraform.zip
|
||||
mv $(TOOLCHAIN_DIR)/temp-terraform/terraform$(EXE_EXTENSION) $(TOOLCHAIN_BIN)/terraform$(EXE_EXTENSION)
|
||||
rm -rf $(TOOLCHAIN_DIR)/temp-terraform/
|
||||
|
||||
build/toolchain/bin/protoc$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
curl -o $(TOOLCHAIN_DIR)/protoc-temp.zip -L $(PROTOC_PACKAGE)
|
||||
(cd $(TOOLCHAIN_DIR); unzip -q -o protoc-temp.zip)
|
||||
rm $(TOOLCHAIN_DIR)/protoc-temp.zip $(TOOLCHAIN_DIR)/readme.txt
|
||||
|
||||
build/toolchain/bin/protoc-gen-doc$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build -i -pkgdir . github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc
|
||||
|
||||
build/toolchain/bin/protoc-gen-go$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build -i -pkgdir . github.com/golang/protobuf/protoc-gen-go
|
||||
|
||||
build/toolchain/bin/protoc-gen-grpc-gateway$(EXE_EXTENSION):
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build -i -pkgdir . github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
|
||||
|
||||
build/toolchain/bin/protoc-gen-swagger$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build -i -pkgdir . github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
|
||||
|
||||
build/toolchain/bin/certgen$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build $(REPOSITORY_ROOT)/tools/certgen/
|
||||
|
||||
build/toolchain/bin/reaper$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build $(REPOSITORY_ROOT)/tools/reaper/
|
||||
|
||||
# Fake target for docker
|
||||
docker: no-sudo
|
||||
|
||||
# Fake target for gcloud
|
||||
gcloud: no-sudo
|
||||
|
||||
tls-certs: install/helm/open-match/secrets/
|
||||
|
||||
install/helm/open-match/secrets/: install/helm/open-match/secrets/tls/root-ca/ install/helm/open-match/secrets/tls/server/
|
||||
|
||||
install/helm/open-match/secrets/tls/root-ca/: build/toolchain/bin/certgen$(EXE_EXTENSION)
|
||||
mkdir -p $(OPEN_MATCH_SECRETS_DIR)/tls/root-ca
|
||||
$(CERTGEN) -ca=true -publiccertificate=$(OPEN_MATCH_SECRETS_DIR)/tls/root-ca/public.cert -privatekey=$(OPEN_MATCH_SECRETS_DIR)/tls/root-ca/private.key
|
||||
|
||||
install/helm/open-match/secrets/tls/server/: build/toolchain/bin/certgen$(EXE_EXTENSION) install/helm/open-match/secrets/tls/root-ca/
|
||||
mkdir -p $(OPEN_MATCH_SECRETS_DIR)/tls/server/
|
||||
$(CERTGEN) -publiccertificate=$(OPEN_MATCH_SECRETS_DIR)/tls/server/public.cert -privatekey=$(OPEN_MATCH_SECRETS_DIR)/tls/server/private.key -rootpubliccertificate=$(OPEN_MATCH_SECRETS_DIR)/tls/root-ca/public.cert -rootprivatekey=$(OPEN_MATCH_SECRETS_DIR)/tls/root-ca/private.key
|
||||
|
||||
auth-docker: gcloud docker
|
||||
$(GCLOUD) $(GCP_PROJECT_FLAG) auth configure-docker
|
||||
|
||||
auth-gke-cluster: gcloud
|
||||
$(GCLOUD) $(GCP_PROJECT_FLAG) container clusters get-credentials $(GKE_CLUSTER_NAME) $(GCP_LOCATION_FLAG)
|
||||
|
||||
activate-gcp-apis: gcloud
|
||||
$(GCLOUD) services enable containerregistry.googleapis.com
|
||||
$(GCLOUD) services enable container.googleapis.com
|
||||
$(GCLOUD) services enable containeranalysis.googleapis.com
|
||||
$(GCLOUD) services enable binaryauthorization.googleapis.com
|
||||
|
||||
create-gcp-service-account: gcloud
|
||||
gcloud $(GCP_PROJECT_FLAG) iam service-accounts create open-match --display-name="Open Match Service Account"
|
||||
gcloud $(GCP_PROJECT_FLAG) iam service-accounts add-iam-policy-binding --member=open-match@$(GCP_PROJECT_ID).iam.gserviceaccount.com --role=roles/container.clusterAdmin
|
||||
gcloud $(GCP_PROJECT_FLAG) iam service-accounts keys create ~/key.json --iam-account open-match@$(GCP_PROJECT_ID).iam.gserviceaccount.com
|
||||
|
||||
create-kind-cluster: build/toolchain/bin/kind$(EXE_EXTENSION) build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
$(KIND) create cluster
|
||||
|
||||
get-kind-kubeconfig: build/toolchain/bin/kind$(EXE_EXTENSION)
|
||||
@echo "============================================="
|
||||
@echo "= Run this command"
|
||||
@echo "============================================="
|
||||
@echo "export KUBECONFIG=\"$(shell $(KIND) get kubeconfig-path)\""
|
||||
@echo "============================================="
|
||||
|
||||
delete-kind-cluster: build/toolchain/bin/kind$(EXE_EXTENSION) build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
-$(KIND) delete cluster
|
||||
|
||||
create-cluster-role-binding:
|
||||
$(KUBECTL) create clusterrolebinding myname-cluster-admin-binding --clusterrole=cluster-admin --user=$(GCLOUD_ACCOUNT_EMAIL)
|
||||
|
||||
create-gke-cluster: GKE_VERSION = 1.15.12-gke.20 # gcloud beta container get-server-config --zone us-west1-a
|
||||
create-gke-cluster: GKE_CLUSTER_SHAPE_FLAGS = --machine-type n1-standard-4 --enable-autoscaling --min-nodes 1 --num-nodes 2 --max-nodes 10 --disk-size 50
|
||||
create-gke-cluster: GKE_FUTURE_COMPAT_FLAGS = --no-enable-basic-auth --no-issue-client-certificate --enable-ip-alias --metadata disable-legacy-endpoints=true --enable-autoupgrade
|
||||
create-gke-cluster: build/toolchain/bin/kubectl$(EXE_EXTENSION) gcloud
|
||||
$(GCLOUD) beta $(GCP_PROJECT_FLAG) container clusters create $(GKE_CLUSTER_NAME) $(GCP_LOCATION_FLAG) $(GKE_CLUSTER_SHAPE_FLAGS) $(GKE_FUTURE_COMPAT_FLAGS) $(GKE_CLUSTER_FLAGS) \
|
||||
--enable-pod-security-policy \
|
||||
--cluster-version $(GKE_VERSION) \
|
||||
--image-type cos_containerd \
|
||||
--tags open-match
|
||||
$(MAKE) create-cluster-role-binding
|
||||
|
||||
|
||||
delete-gke-cluster: gcloud
|
||||
-$(GCLOUD) $(GCP_PROJECT_FLAG) container clusters delete $(GKE_CLUSTER_NAME) $(GCP_LOCATION_FLAG) $(GCLOUD_EXTRA_FLAGS)
|
||||
|
||||
create-mini-cluster: build/toolchain/bin/minikube$(EXE_EXTENSION)
|
||||
$(MINIKUBE) start --memory 6144 --cpus 4 --disk-size 50g
|
||||
|
||||
delete-mini-cluster: build/toolchain/bin/minikube$(EXE_EXTENSION)
|
||||
-$(MINIKUBE) delete
|
||||
|
||||
gcp-apply-binauthz-policy: build/policies/binauthz.yaml
|
||||
$(GCLOUD) beta $(GCP_PROJECT_FLAG) container binauthz policy import build/policies/binauthz.yaml
|
||||
|
||||
all-protos: $(ALL_PROTOS)
|
||||
|
||||
# The proto generator really wants to be run from the $GOPATH root, and doesn't
|
||||
# support methods for directing it to the correct location that's not the proto
|
||||
# file's location. So instead put it in a tempororary directory, then move it
|
||||
# out.
|
||||
pkg/pb/%.pb.go: api/%.proto third_party/ build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-go$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-grpc-gateway$(EXE_EXTENSION)
|
||||
mkdir -p $(REPOSITORY_ROOT)/build/prototmp $(REPOSITORY_ROOT)/pkg/pb
|
||||
$(PROTOC) $< \
|
||||
-I $(REPOSITORY_ROOT) -I $(PROTOC_INCLUDES) \
|
||||
--go_out=plugins=grpc:$(REPOSITORY_ROOT)/build/prototmp
|
||||
mv $(REPOSITORY_ROOT)/build/prototmp/open-match.dev/open-match/$@ $@
|
||||
|
||||
internal/ipb/%.pb.go: internal/api/%.proto third_party/ build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-go$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-grpc-gateway$(EXE_EXTENSION)
|
||||
mkdir -p $(REPOSITORY_ROOT)/build/prototmp $(REPOSITORY_ROOT)/internal/ipb
|
||||
$(PROTOC) $< \
|
||||
-I $(REPOSITORY_ROOT) -I $(PROTOC_INCLUDES) \
|
||||
--go_out=plugins=grpc:$(REPOSITORY_ROOT)/build/prototmp
|
||||
mv $(REPOSITORY_ROOT)/build/prototmp/open-match.dev/open-match/$@ $@
|
||||
|
||||
pkg/pb/%.pb.gw.go: api/%.proto third_party/ build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-go$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-grpc-gateway$(EXE_EXTENSION)
|
||||
mkdir -p $(REPOSITORY_ROOT)/build/prototmp $(REPOSITORY_ROOT)/pkg/pb
|
||||
$(PROTOC) $< \
|
||||
-I $(REPOSITORY_ROOT) -I $(PROTOC_INCLUDES) \
|
||||
--grpc-gateway_out=logtostderr=true,allow_delete_body=true:$(REPOSITORY_ROOT)/build/prototmp
|
||||
mv $(REPOSITORY_ROOT)/build/prototmp/open-match.dev/open-match/$@ $@
|
||||
|
||||
api/%.swagger.json: api/%.proto third_party/ build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-swagger$(EXE_EXTENSION)
|
||||
$(PROTOC) $< \
|
||||
-I $(REPOSITORY_ROOT) -I $(PROTOC_INCLUDES) \
|
||||
--swagger_out=logtostderr=true,allow_delete_body=true:$(REPOSITORY_ROOT)
|
||||
|
||||
api/api.md: third_party/ build/toolchain/bin/protoc-gen-doc$(EXE_EXTENSION)
|
||||
$(PROTOC) api/*.proto \
|
||||
-I $(REPOSITORY_ROOT) -I $(PROTOC_INCLUDES) \
|
||||
--doc_out=. \
|
||||
--doc_opt=markdown,api_temp.md
|
||||
# Crazy hack that insert hugo link reference to this API doc -)
|
||||
cat ./docs/hugo_apiheader.txt ./api_temp.md >> api.md
|
||||
mv ./api.md $(REPOSITORY_ROOT)/../open-match-docs/site/content/en/docs/Reference/
|
||||
rm ./api_temp.md
|
||||
|
||||
# Include structure of the protos needs to be called out do the dependency chain is run through properly.
|
||||
pkg/pb/backend.pb.go: pkg/pb/messages.pb.go
|
||||
pkg/pb/frontend.pb.go: pkg/pb/messages.pb.go
|
||||
pkg/pb/matchfunction.pb.go: pkg/pb/messages.pb.go
|
||||
pkg/pb/query.pb.go: pkg/pb/messages.pb.go
|
||||
pkg/pb/evaluator.pb.go: pkg/pb/messages.pb.go
|
||||
internal/ipb/synchronizer.pb.go: pkg/pb/messages.pb.go
|
||||
|
||||
build: assets
|
||||
$(GO) build ./...
|
||||
$(GO) build -tags e2ecluster ./...
|
||||
|
||||
define test_folder
|
||||
$(if $(wildcard $(1)/go.mod), \
|
||||
cd $(1) && \
|
||||
$(GO) test -cover -test.count $(GOLANG_TEST_COUNT) -race ./... && \
|
||||
$(GO) test -cover -test.count $(GOLANG_TEST_COUNT) -run IgnoreRace$$ ./... \
|
||||
)
|
||||
$(foreach dir, $(wildcard $(1)/*/.), $(call test_folder, $(dir)))
|
||||
endef
|
||||
|
||||
define fast_test_folder
|
||||
$(if $(wildcard $(1)/go.mod), \
|
||||
cd $(1) && \
|
||||
$(GO) test ./... \
|
||||
)
|
||||
$(foreach dir, $(wildcard $(1)/*/.), $(call fast_test_folder, $(dir)))
|
||||
endef
|
||||
|
||||
test: $(ALL_PROTOS) tls-certs third_party/
|
||||
$(call test_folder,.)
|
||||
|
||||
fasttest: $(ALL_PROTOS) tls-certs third_party/
|
||||
$(call fast_test_folder,.)
|
||||
|
||||
test-e2e-cluster: all-protos tls-certs third_party/
|
||||
$(HELM) test --timeout 7m30s -v 0 --logs -n $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(OPEN_MATCH_HELM_NAME)
|
||||
|
||||
fmt:
|
||||
$(GO) fmt ./...
|
||||
gofmt -s -w .
|
||||
|
||||
vet:
|
||||
$(GO) vet ./...
|
||||
|
||||
golangci: build/toolchain/bin/golangci-lint$(EXE_EXTENSION)
|
||||
GO111MODULE=on $(GOLANGCI) run --config=$(REPOSITORY_ROOT)/.golangci.yaml
|
||||
|
||||
lint: fmt vet golangci lint-chart terraform-lint
|
||||
|
||||
assets: $(ALL_PROTOS) tls-certs third_party/ build/chart/
|
||||
|
||||
build/cmd: $(foreach CMD,$(CMDS),build/cmd/$(CMD))
|
||||
|
||||
# Building a given build/cmd folder is split into two pieces: BUILD_PHONY and
|
||||
# COPY_PHONY. The BUILD_PHONY is the common go build command, which is
|
||||
# reusable. The COPY_PHONY is used by some targets which require additional
|
||||
# files to be included in the image.
|
||||
$(foreach CMD,$(CMDS),build/cmd/$(CMD)): build/cmd/%: build/cmd/%/BUILD_PHONY build/cmd/%/COPY_PHONY
|
||||
|
||||
build/cmd/%/BUILD_PHONY:
|
||||
mkdir -p $(BUILD_DIR)/cmd/$*
|
||||
CGO_ENABLED=0 $(GO) build -a -installsuffix cgo -o $(BUILD_DIR)/cmd/$*/run open-match.dev/open-match/cmd/$*
|
||||
|
||||
# Default is that nothing needs to be copied into the direcotry
|
||||
build/cmd/%/COPY_PHONY:
|
||||
#
|
||||
|
||||
build/cmd/swaggerui/COPY_PHONY:
|
||||
mkdir -p $(BUILD_DIR)/cmd/swaggerui/static/api
|
||||
cp third_party/swaggerui/* $(BUILD_DIR)/cmd/swaggerui/static/
|
||||
$(SED_REPLACE) 's|https://open-match.dev/api/v.*/|/api/|g' $(BUILD_DIR)/cmd/swaggerui/static/config.json
|
||||
cp api/*.json $(BUILD_DIR)/cmd/swaggerui/static/api/
|
||||
|
||||
build/cmd/demo-%/COPY_PHONY:
|
||||
mkdir -p $(BUILD_DIR)/cmd/demo-$*/
|
||||
cp -r examples/demo/static $(BUILD_DIR)/cmd/demo-$*/static
|
||||
|
||||
build/policies/binauthz.yaml: install/policies/binauthz.yaml
|
||||
mkdir -p $(BUILD_DIR)/policies
|
||||
cp -f $(REPOSITORY_ROOT)/install/policies/binauthz.yaml $(BUILD_DIR)/policies/binauthz.yaml
|
||||
$(SED_REPLACE) 's/$$PROJECT_ID/$(GCP_PROJECT_ID)/g' $(BUILD_DIR)/policies/binauthz.yaml
|
||||
$(SED_REPLACE) 's/$$GKE_CLUSTER_NAME/$(GKE_CLUSTER_NAME)/g' $(BUILD_DIR)/policies/binauthz.yaml
|
||||
$(SED_REPLACE) 's/$$GCP_LOCATION/$(GCP_LOCATION)/g' $(BUILD_DIR)/policies/binauthz.yaml
|
||||
ifeq ($(ENABLE_SECURITY_HARDENING),1)
|
||||
$(SED_REPLACE) 's/$$EVALUATION_MODE/ALWAYS_DENY/g' $(BUILD_DIR)/policies/binauthz.yaml
|
||||
else
|
||||
$(SED_REPLACE) 's/$$EVALUATION_MODE/ALWAYS_ALLOW/g' $(BUILD_DIR)/policies/binauthz.yaml
|
||||
endif
|
||||
|
||||
terraform-test: install/terraform/open-match/.terraform/ install/terraform/open-match-build/.terraform/
|
||||
(cd $(REPOSITORY_ROOT)/install/terraform/open-match/ && $(TERRAFORM) validate)
|
||||
(cd $(REPOSITORY_ROOT)/install/terraform/open-match-build/ && $(TERRAFORM) validate)
|
||||
|
||||
terraform-plan: install/terraform/open-match/.terraform/
|
||||
(cd $(REPOSITORY_ROOT)/install/terraform/open-match/ && $(TERRAFORM) plan -var gcp_project_id=$(GCP_PROJECT_ID) -var gcp_location=$(GCP_LOCATION))
|
||||
|
||||
terraform-lint: build/toolchain/bin/terraform$(EXE_EXTENSION)
|
||||
$(TERRAFORM) fmt -recursive
|
||||
|
||||
terraform-apply: install/terraform/open-match/.terraform/
|
||||
(cd $(REPOSITORY_ROOT)/install/terraform/open-match/ && $(TERRAFORM) apply -var gcp_project_id=$(GCP_PROJECT_ID) -var gcp_location=$(GCP_LOCATION))
|
||||
|
||||
install/terraform/open-match/.terraform/: build/toolchain/bin/terraform$(EXE_EXTENSION)
|
||||
(cd $(REPOSITORY_ROOT)/install/terraform/open-match/ && $(TERRAFORM) init)
|
||||
|
||||
install/terraform/open-match-build/.terraform/: build/toolchain/bin/terraform$(EXE_EXTENSION)
|
||||
(cd $(REPOSITORY_ROOT)/install/terraform/open-match-build/ && $(TERRAFORM) init)
|
||||
|
||||
build/certificates/: build/toolchain/bin/certgen$(EXE_EXTENSION)
|
||||
mkdir -p $(BUILD_DIR)/certificates/
|
||||
cd $(BUILD_DIR)/certificates/ && $(CERTGEN)
|
||||
|
||||
md-test: docker
|
||||
docker run -t --rm -v $(REPOSITORY_ROOT):/mnt:ro dkhamsing/awesome_bot --white-list "localhost,https://goreportcard.com,github.com/googleforgames/open-match/tree/release-,github.com/googleforgames/open-match/blob/release-,github.com/googleforgames/open-match/releases/download/v,https://swagger.io/tools/swagger-codegen/" --allow-dupe --allow-redirect --skip-save-results `find . -type f -name '*.md' -not -path './build/*' -not -path './.git*'`
|
||||
|
||||
ci-deploy-artifacts: install/yaml/ $(SWAGGER_JSON_DOCS) build/chart/ gcloud
|
||||
ifeq ($(_GCB_POST_SUBMIT),1)
|
||||
gsutil cp -a public-read $(REPOSITORY_ROOT)/install/yaml/* gs://open-match-chart/install/v$(BASE_VERSION)/yaml/
|
||||
gsutil cp -a public-read $(REPOSITORY_ROOT)/api/*.json gs://open-match-chart/api/v$(BASE_VERSION)/
|
||||
# Deploy Helm Chart
|
||||
# Since each build will refresh just it's version we can allow this for every post submit.
|
||||
# Copy the files into multiple locations to keep a backup.
|
||||
gsutil cp -a public-read $(BUILD_DIR)/chart/*.* gs://open-match-chart/chart/by-hash/$(VERSION)/
|
||||
gsutil cp -a public-read $(BUILD_DIR)/chart/*.* gs://open-match-chart/chart/
|
||||
else
|
||||
@echo "Not deploying build artifacts to open-match.dev because this is not a post commit change."
|
||||
endif
|
||||
|
||||
ci-reap-namespaces: build/toolchain/bin/reaper$(EXE_EXTENSION)
|
||||
-$(TOOLCHAIN_BIN)/reaper -age=30m
|
||||
|
||||
# For presubmit we want to update the protobuf generated files and verify that tests are good.
|
||||
presubmit: GOLANG_TEST_COUNT = 5
|
||||
presubmit: clean third_party/ update-chart-deps assets update-deps lint build test md-test terraform-test
|
||||
|
||||
build/release/: presubmit clean-install-yaml install/yaml/
|
||||
mkdir -p $(BUILD_DIR)/release/
|
||||
cp $(REPOSITORY_ROOT)/install/yaml/* $(BUILD_DIR)/release/
|
||||
|
||||
validate-preview-release:
|
||||
ifneq ($(_GCB_POST_SUBMIT),1)
|
||||
@echo "You must run make with _GCB_POST_SUBMIT=1"
|
||||
exit 1
|
||||
endif
|
||||
ifneq (,$(findstring -preview,$(BASE_VERSION)))
|
||||
@echo "Creating preview for $(BASE_VERSION)"
|
||||
else
|
||||
@echo "BASE_VERSION must contain -preview, it is $(BASE_VERSION)"
|
||||
exit 1
|
||||
endif
|
||||
|
||||
preview-release: REGISTRY = gcr.io/$(OPEN_MATCH_PUBLIC_IMAGES_PROJECT_ID)
|
||||
preview-release: TAG = $(BASE_VERSION)
|
||||
preview-release: validate-preview-release build/release/ retag-images ci-deploy-artifacts
|
||||
|
||||
release: REGISTRY = gcr.io/$(OPEN_MATCH_PUBLIC_IMAGES_PROJECT_ID)
|
||||
release: TAG = $(BASE_VERSION)
|
||||
release: presubmit build/release/
|
||||
|
||||
clean-secrets:
|
||||
rm -rf $(OPEN_MATCH_SECRETS_DIR)
|
||||
|
||||
clean-protos:
|
||||
rm -rf $(REPOSITORY_ROOT)/build/prototmp/
|
||||
rm -rf $(REPOSITORY_ROOT)/pkg/pb/
|
||||
rm -rf $(REPOSITORY_ROOT)/internal/ipb/
|
||||
|
||||
clean-terraform:
|
||||
rm -rf $(REPOSITORY_ROOT)/install/terraform/.terraform/
|
||||
|
||||
clean-build: clean-toolchain clean-release clean-chart
|
||||
rm -rf $(BUILD_DIR)/
|
||||
|
||||
clean-release:
|
||||
rm -rf $(BUILD_DIR)/release/
|
||||
|
||||
clean-toolchain:
|
||||
rm -rf $(TOOLCHAIN_DIR)/
|
||||
|
||||
clean-chart:
|
||||
rm -rf $(BUILD_DIR)/chart/
|
||||
|
||||
clean-install-yaml:
|
||||
rm -f $(REPOSITORY_ROOT)/install/yaml/*
|
||||
|
||||
clean-swagger-docs:
|
||||
rm -rf $(REPOSITORY_ROOT)/api/*.json
|
||||
|
||||
clean-third-party:
|
||||
rm -rf $(REPOSITORY_ROOT)/third_party/
|
||||
|
||||
clean: clean-images clean-build clean-install-yaml clean-secrets clean-terraform clean-third-party clean-protos clean-swagger-docs
|
||||
|
||||
proxy-frontend: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
@echo "Frontend Health: http://localhost:$(FRONTEND_PORT)/healthz"
|
||||
@echo "Frontend RPC: http://localhost:$(FRONTEND_PORT)/debug/rpcz"
|
||||
@echo "Frontend Trace: http://localhost:$(FRONTEND_PORT)/debug/tracez"
|
||||
$(KUBECTL) port-forward --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(shell $(KUBECTL) get pod --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --selector="app=open-match,component=frontend,release=$(OPEN_MATCH_HELM_NAME)" --output jsonpath='{.items[0].metadata.name}') $(FRONTEND_PORT):51504 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
proxy-backend: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
@echo "Backend Health: http://localhost:$(BACKEND_PORT)/healthz"
|
||||
@echo "Backend RPC: http://localhost:$(BACKEND_PORT)/debug/rpcz"
|
||||
@echo "Backend Trace: http://localhost:$(BACKEND_PORT)/debug/tracez"
|
||||
$(KUBECTL) port-forward --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(shell $(KUBECTL) get pod --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --selector="app=open-match,component=backend,release=$(OPEN_MATCH_HELM_NAME)" --output jsonpath='{.items[0].metadata.name}') $(BACKEND_PORT):51505 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
proxy-query: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
@echo "QueryService Health: http://localhost:$(QUERY_PORT)/healthz"
|
||||
@echo "QueryService RPC: http://localhost:$(QUERY_PORT)/debug/rpcz"
|
||||
@echo "QueryService Trace: http://localhost:$(QUERY_PORT)/debug/tracez"
|
||||
$(KUBECTL) port-forward --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(shell $(KUBECTL) get pod --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --selector="app=open-match,component=query,release=$(OPEN_MATCH_HELM_NAME)" --output jsonpath='{.items[0].metadata.name}') $(QUERY_PORT):51503 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
proxy-synchronizer: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
@echo "Synchronizer Health: http://localhost:$(SYNCHRONIZER_PORT)/healthz"
|
||||
@echo "Synchronizer RPC: http://localhost:$(SYNCHRONIZER_PORT)/debug/rpcz"
|
||||
@echo "Synchronizer Trace: http://localhost:$(SYNCHRONIZER_PORT)/debug/tracez"
|
||||
$(KUBECTL) port-forward --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(shell $(KUBECTL) get pod --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --selector="app=open-match,component=synchronizer,release=$(OPEN_MATCH_HELM_NAME)" --output jsonpath='{.items[0].metadata.name}') $(SYNCHRONIZER_PORT):51506 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
proxy-jaeger: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
@echo "Jaeger Query Frontend: http://localhost:16686"
|
||||
$(KUBECTL) port-forward --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(shell $(KUBECTL) get pod --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --selector="app.kubernetes.io/name=jaeger,app.kubernetes.io/component=query" --output jsonpath='{.items[0].metadata.name}') $(JAEGER_QUERY_PORT):16686 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
proxy-grafana: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
@echo "User: admin"
|
||||
@echo "Password: openmatch"
|
||||
$(KUBECTL) port-forward --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(shell $(KUBECTL) get pod --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --selector="app=grafana,release=$(OPEN_MATCH_HELM_NAME)" --output jsonpath='{.items[0].metadata.name}') $(GRAFANA_PORT):3000 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
proxy-prometheus: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
$(KUBECTL) port-forward --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(shell $(KUBECTL) get pod --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --selector="app=prometheus,component=server,release=$(OPEN_MATCH_HELM_NAME)" --output jsonpath='{.items[0].metadata.name}') $(PROMETHEUS_PORT):9090 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
proxy-dashboard: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
$(KUBECTL) port-forward --namespace kube-system $(shell $(KUBECTL) get pod --namespace kube-system --selector="app=kubernetes-dashboard" --output jsonpath='{.items[0].metadata.name}') $(DASHBOARD_PORT):9092 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
proxy-ui: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
@echo "SwaggerUI Health: http://localhost:$(SWAGGERUI_PORT)/"
|
||||
$(KUBECTL) port-forward --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(shell $(KUBECTL) get pod --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --selector="app=open-match,component=swaggerui,release=$(OPEN_MATCH_HELM_NAME)" --output jsonpath='{.items[0].metadata.name}') $(SWAGGERUI_PORT):51500 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
proxy-demo: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
@echo "View Demo: http://localhost:$(DEMO_PORT)"
|
||||
$(KUBECTL) port-forward --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE)-demo $(shell $(KUBECTL) get pod --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE)-demo --selector="app=open-match-demo,component=demo" --output jsonpath='{.items[0].metadata.name}') $(DEMO_PORT):51507 $(PORT_FORWARD_ADDRESS_FLAG)
|
||||
|
||||
# Run `make proxy` instead to run everything at the same time.
|
||||
# If you run this directly it will just run each proxy sequentially.
|
||||
proxy-all: proxy-frontend proxy-backend proxy-query proxy-grafana proxy-prometheus proxy-jaeger proxy-synchronizer proxy-ui proxy-dashboard proxy-demo
|
||||
|
||||
proxy:
|
||||
# This is an exception case where we'll call recursive make.
|
||||
# To simplify accessing all the proxy ports we'll call `make proxy-all` with enough subprocesses to run them concurrently.
|
||||
$(MAKE) proxy-all -j20
|
||||
|
||||
update-deps:
|
||||
$(GO) mod tidy
|
||||
|
||||
third_party/: third_party/google/api third_party/protoc-gen-swagger/options third_party/swaggerui/
|
||||
|
||||
third_party/google/api:
|
||||
mkdir -p $(TOOLCHAIN_DIR)/googleapis-temp/
|
||||
mkdir -p $(REPOSITORY_ROOT)/third_party/google/api
|
||||
mkdir -p $(REPOSITORY_ROOT)/third_party/google/rpc
|
||||
curl -o $(TOOLCHAIN_DIR)/googleapis-temp/googleapis.zip -L https://github.com/googleapis/googleapis/archive/$(GOOGLE_APIS_VERSION).zip
|
||||
(cd $(TOOLCHAIN_DIR)/googleapis-temp/; unzip -q -o googleapis.zip)
|
||||
cp -f $(TOOLCHAIN_DIR)/googleapis-temp/googleapis-$(GOOGLE_APIS_VERSION)/google/api/*.proto $(REPOSITORY_ROOT)/third_party/google/api/
|
||||
cp -f $(TOOLCHAIN_DIR)/googleapis-temp/googleapis-$(GOOGLE_APIS_VERSION)/google/rpc/*.proto $(REPOSITORY_ROOT)/third_party/google/rpc/
|
||||
rm -rf $(TOOLCHAIN_DIR)/googleapis-temp
|
||||
|
||||
third_party/protoc-gen-swagger/options:
|
||||
mkdir -p $(TOOLCHAIN_DIR)/grpc-gateway-temp/
|
||||
mkdir -p $(REPOSITORY_ROOT)/third_party/protoc-gen-swagger/options
|
||||
curl -o $(TOOLCHAIN_DIR)/grpc-gateway-temp/grpc-gateway.zip -L https://github.com/grpc-ecosystem/grpc-gateway/archive/v$(GRPC_GATEWAY_VERSION).zip
|
||||
(cd $(TOOLCHAIN_DIR)/grpc-gateway-temp/; unzip -q -o grpc-gateway.zip)
|
||||
cp -f $(TOOLCHAIN_DIR)/grpc-gateway-temp/grpc-gateway-$(GRPC_GATEWAY_VERSION)/protoc-gen-swagger/options/*.proto $(REPOSITORY_ROOT)/third_party/protoc-gen-swagger/options/
|
||||
rm -rf $(TOOLCHAIN_DIR)/grpc-gateway-temp
|
||||
|
||||
third_party/swaggerui/:
|
||||
mkdir -p $(TOOLCHAIN_DIR)/swaggerui-temp/
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
curl -o $(TOOLCHAIN_DIR)/swaggerui-temp/swaggerui.zip -L \
|
||||
https://github.com/swagger-api/swagger-ui/archive/v$(SWAGGERUI_VERSION).zip
|
||||
(cd $(TOOLCHAIN_DIR)/swaggerui-temp/; unzip -q -o swaggerui.zip)
|
||||
cp -rf $(TOOLCHAIN_DIR)/swaggerui-temp/swagger-ui-$(SWAGGERUI_VERSION)/dist/ \
|
||||
$(REPOSITORY_ROOT)/third_party/swaggerui
|
||||
# Update the URL in the main page to point to a known good endpoint.
|
||||
cp $(REPOSITORY_ROOT)/cmd/swaggerui/config.json $(REPOSITORY_ROOT)/third_party/swaggerui/
|
||||
$(SED_REPLACE) 's|url:.*|configUrl: "/config.json",|g' $(REPOSITORY_ROOT)/third_party/swaggerui/index.html
|
||||
$(SED_REPLACE) 's|0.0.0-dev|$(BASE_VERSION)|g' $(REPOSITORY_ROOT)/third_party/swaggerui/config.json
|
||||
rm -rf $(TOOLCHAIN_DIR)/swaggerui-temp
|
||||
|
||||
sync-deps:
|
||||
$(GO) clean -modcache
|
||||
$(GO) mod download
|
||||
|
||||
# Prevents users from running with sudo.
|
||||
# There's an exception for Google Cloud Build because it runs as root.
|
||||
no-sudo:
|
||||
ifndef OPEN_MATCH_CI_MODE
|
||||
ifeq ($(shell whoami),root)
|
||||
@echo "ERROR: Running Makefile as root (or sudo)"
|
||||
@echo "Please follow the instructions at https://docs.docker.com/install/linux/linux-postinstall/ if you are trying to sudo run the Makefile because of the 'Cannot connect to the Docker daemon' error."
|
||||
@echo "NOTE: sudo/root do not have the authentication token to talk to any GCP service via gcloud."
|
||||
exit 1
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: docker gcloud update-deps sync-deps all build proxy-dashboard proxy-prometheus proxy-grafana clean clean-build clean-toolchain clean-binaries clean-protos presubmit test ci-reap-namespaces md-test vet
|
221
README.md
221
README.md
@ -1,214 +1,39 @@
|
||||
# Open Match
|
||||

|
||||
|
||||
Open Match is an open source game matchmaker designed to allow game creators to re-use a common matchmaker framework. It’s designed to be flexible (run it anywhere Kubernetes runs), extensible (match logic can be customized to work for any game), and scalable.
|
||||
[](https://godoc.org/open-match.dev/open-match)
|
||||
[](https://goreportcard.com/report/open-match.dev/open-match)
|
||||
[](https://github.com/googleforgames/open-match/blob/master/LICENSE)
|
||||
[](https://github.com/googleforgames/open-match/releases)
|
||||
[](https://twitter.com/intent/follow?screen_name=Open_Match)
|
||||
|
||||
Matchmaking is a complicated process, and when large player populations are involved, many popular matchmaking approaches touch on significant areas of computer science including graph theory and massively concurrent processing. Open Match is an effort to provide a foundation upon which these difficult problems can be addressed by the wider game development community. As Josh Menke — famous for working on matchmaking for many popular triple-A franchises — put it:
|
||||
Open Match is an open source game matchmaking framework that simplifies building
|
||||
a scalable and extensible Matchmaker. It is designed to give the game developer
|
||||
full control over how to make matches while removing the burden of dealing with
|
||||
the challenges of running a production service at scale.
|
||||
|
||||
["Matchmaking, a lot of it actually really is just really good engineering. There's a lot of really hard networking and plumbing problems that need to be solved, depending on the size of your audience."](https://youtu.be/-pglxege-gU?t=830)
|
||||
Please visit [Open Match website](https://open-match.dev/site/docs/) for user
|
||||
documentation, demo instructions etc.
|
||||
|
||||
## Contributing to Open Match
|
||||
|
||||
This project attempts to solve the networking and plumbing problems, so game developers can focus on the logic to match players into great games.
|
||||
Open Match is in active development and we would love your contribution! Please
|
||||
read the [contributing guide](CONTRIBUTING.md) for guidelines on contributing to
|
||||
Open Match.
|
||||
|
||||
## Disclaimer
|
||||
This software is currently alpha, and subject to change. **It is not yet ready to be used in production.**
|
||||
The [Open Match Development guide](docs/development.md) has detailed instructions
|
||||
on getting the source code, making changes, testing and submitting a pull request
|
||||
to Open Match.
|
||||
|
||||
# Core Concepts
|
||||
## Support
|
||||
|
||||
[Watch the introduction of Open Match at Unite Berlin 2018 on YouTube](https://youtu.be/qasAmy_ko2o)
|
||||
|
||||
Open Match is designed to support massively concurrent matchmaking, and to be scalable to player populations of hundreds of millions or more. It attempts to apply stateless web tech microservices patterns to game matchmaking. If you're not sure what that means, that's okay — it is fully open source and designed to be customizable to fit into your online game architecture — so have a look a the code and modify it as you see fit.
|
||||
|
||||
## Glossary
|
||||
|
||||
* **MMF** — Matchmaking function. This is the customizable matchmaking logic.
|
||||
* **Component** — One of the discrete processes in an Open Match deployment. Open Match is composed of multiple scalable microservices called 'components'.
|
||||
* **Roster** — A list of all the players in a match.
|
||||
* **Profile** — The json blob containing all the parameters used to select which players go into a roster.
|
||||
* **Match Object** — A json blob to contain the results of the matchmaking function. Sent with an empty roster section to the backend API from your game backend and then returned with the matchmaking results filled in.
|
||||
* **MMFOrc** — Matchmaker function orchestrator. This Open Match core component is in charge of kicking off custom matchmaking functions (MMFs) and evaluator processes.
|
||||
* **State Storage** — The storage software used by Open Match to hold all the matchmaking state. Open Match ships with [Redis](https://redis.io/) as the default state storage.
|
||||
* **Assignment** — Refers to assigning a player or group of players to a dedicated game server instance. Open Match offers a path to send dedicated game server connection details from your backend to your game clients after a match has been made.
|
||||
|
||||
## Requirements
|
||||
* [Kubernetes](https://kubernetes.io/) cluster — tested with version 1.9.
|
||||
* [Redis 4+](https://redis.io/) — tested with 4.0.11.
|
||||
* Open Match is compiled against the latest release of [Golang](https://golang.org/) — tested with 1.10.3.
|
||||
|
||||
## Components
|
||||
|
||||
Open Match is a set of processes designed to run on Kubernetes. It contains these **core** components:
|
||||
|
||||
1. Frontend API
|
||||
1. Backend API
|
||||
1. Matchmaker Function Orchestrator (MMFOrc)
|
||||
|
||||
It also explicitly depends on these two **customizable** components.
|
||||
|
||||
1. Matchmaking "Function" (MMF)
|
||||
1. Evaluator
|
||||
|
||||
While **core** components are fully open source and *can* be modified, they are designed to support the majority of matchmaking scenarios *without need to change the source code*. The Open Match repository ships with simple **customizable** example MMF and Evaluator processes, but it is expected that most users will want full control over the logic in these, so they have been designed to be as easy to modify or replace as possible.
|
||||
|
||||
### Frontend API
|
||||
|
||||
The Frontend API accepts the player data and puts it in state storage so your Matchmaking Function (MMF) can access it.
|
||||
|
||||
The Frontend API is a server application that implements the [gRPC](https://grpc.io/) service defined in `api/protobuf-spec/frontend.proto`. At the most basic level, it expects clients to connect and send:
|
||||
* A **unique ID** for the group of players (the group can contain any number of players, including only one).
|
||||
* A **json blob** containing all player-related data you want to use in your matchmaking function.
|
||||
|
||||
The client is expected to maintain a connection, waiting for an update from the API that contains the details required to connect to a dedicated game server instance (an 'assignment'). There are also basic functions for removing an ID from the matchmaking pool or an existing match.
|
||||
|
||||
### Backend API
|
||||
|
||||
The Backend API puts match profiles in state storage which the Matchmaking Function (MMF) can access and use to decide which players should be put into a match together, then return those matches to dedicated game server instances.
|
||||
|
||||
The Backend API is a server application that implements the [gRPC](https://grpc.io/) service defined in `api/protobuf-spec/backend.proto`. At the most basic level, it expects to be connected to your online infrastructure (probably to your server scaling manager or scheduler, or even directly to a dedicated game server), and to receive:
|
||||
* A **unique ID** for a matchmaking profile.
|
||||
* A **json blob** containing all the match-related data you want to use in your matchmaking function, in an 'empty' match object.
|
||||
|
||||
Your game backend is expected to maintain a connection, waiting for 'filled' match objects containing a roster of players. The Backend API also provides a return path for your game backend to return dedicated game server connection details (an 'assignment') to the game client, and to delete these 'assignments'.
|
||||
|
||||
### Matchmaking Function Orchestrator (MMFOrc)
|
||||
|
||||
The MMFOrc kicks off your custom matchmaking function (MMF) for every profile submitted to the Backend API. It also runs the Evaluator to resolve conflicts in case more than one of your profiles matched the same players.
|
||||
|
||||
The MMFOrc exists to orchestrate/schedule your **custom components**, running them as often as required to meet the demands of your game. MMFOrc runs in an endless loop, submitting MMFs and Evaluator jobs to Kubernetes.
|
||||
|
||||
### Evaluator
|
||||
|
||||
The Evaluator resolves conflicts when multiple matches want to include the same player(s).
|
||||
|
||||
The Evaluator is a component run by the Matchmaker Function Orchestrator (MMFOrc) after the matchmaker functions have been run, and some proposed results are available. The Evaluator looks at all the proposed matches, and if multiple proposals contain the same player(s), it breaks the tie. In many simple matchmaking setups with only a few game modes and matchmaking functions that always look at different parts of the matchmaking pool, the Evaluator may functionally be a no-op or first-in-first-out algorithm. In complex matchmaking setups where, for example, a player can queue for multiple types of matches, the Evaluator provides the critical customizability to evaluate all available proposals and approve those that will passed to your game servers.
|
||||
|
||||
Large-scale concurrent matchmaking functions is a complex topic, and users who wish to do this are encouraged to engage with the [Open Match community](https://github.com/GoogleCloudPlatform/open-match#get-involved) about patterns and best practices.
|
||||
|
||||
### Matchmaking Functions (MMFs)
|
||||
|
||||
Matchmaking Functions (MMFs) are run by the Matchmaker Function Orchestrator (MMFOrc) — once per profile it sees in state storage. The MMF is run as a Job in Kubernetes, and has full access to read and write from state storage. At a high level, the encouraged pattern is to write a MMF in whatever language you are comfortable in that can do the following things:
|
||||
|
||||
1. Read/write from the Open Match state storage — Open Match ships with Redis as the default state storage.
|
||||
1. Be packaged in a (Linux) Docker container.
|
||||
1. Read a profile you wrote to state storage using the Backend API.
|
||||
1. Select from the player data you wrote to state storage using the Frontend API.
|
||||
1. Run your custom logic to try to find a match.
|
||||
1. Write the match object it creates to state storage at a specified key.
|
||||
1. Remove the players it selected from consideration by other MMFs.
|
||||
1. (Optional, but recommended) Export stats for metrics collection.
|
||||
|
||||
Example MMFs are provided in Golang and C#.
|
||||
|
||||
## Open Source Software integrations
|
||||
|
||||
### Structured logging
|
||||
|
||||
Logging for Open Match uses the [Golang logrus module](https://github.com/sirupsen/logrus) to provide structured logs. Logs are output to `stdout` in each component, as expected by Docker and Kubernetes. If you have a specific log aggregator as your final destination, we recommend you have a look at the logrus documentation as there is probably a log formatter that plays nicely with your stack.
|
||||
|
||||
### Instrumentation for metrics
|
||||
|
||||
Open Match uses [OpenCensus](https://opencensus.io/) for metrics instrumentation. The [gRPC](https://grpc.io/) integrations are built-in, and Golang redigo module integrations are incoming, but [haven't been merged into the official repo](https://github.com/opencensus-integrations/redigo/pull/1). All of the core components expose HTTP `/metrics` endpoints on the port defined in `config/matchmaker_config.json` (default: 9555) for Prometheus to scrape. If you would like to export to a different metrics aggregation platform, we suggest you have a look at the OpenCensus documentation — there may be one written for you already, and switching to it may be as simple as changing a few lines of code.
|
||||
|
||||
**Note:** A standard for instrumentation of MMFs is planned.
|
||||
|
||||
### Redis setup
|
||||
|
||||
By default, Open Match expects you to run Redis *somewhere*. Connection information can be put in the config file (`matchmaker_config.json`) for any Redis instance reachable from the [Kubernetes namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/). By default, Open Match sensibly runs in the Kubernetes `default` namespace. In most instances, we expect users will run a copy of Redis in a pod in Kubernetes, with a service pointing to it.
|
||||
|
||||
* HA configurations for Redis aren't implemented by the provided Kubernetes resource definition files, but Open Match expects the Redis service to be named `redis-sentinel`, which provides an easier path to multi-instance deployments.
|
||||
|
||||
## Additional examples
|
||||
|
||||
**Note:** These examples will be expanded on in future releases.
|
||||
|
||||
The following examples of how to call the APIs are provided in the repository. Both have a `Dockerfile` and `cloudbuild.yaml` files in their respective directories:
|
||||
|
||||
* `examples/frontendclient/main.go` acts as a client to the the Frontend API, putting a player into the queue with simulated latencies from major metropolitan cities and a couple of other matchmaking attributes. It then waits for you to manually put a value in Redis to simulate a server connection string being written using the backend API 'CreateAssignments' call, and displays that value on stdout for you to verify.
|
||||
* `examples/backendclient/main.go` calls the Backend API and passes in the profile found in `backendstub/profiles/testprofile.json` to the `ListMatches` API endpoint, then continually prints the results until you exit, or there are insufficient players to make a match based on the profile..
|
||||
|
||||
## Usage
|
||||
|
||||
Documentation and usage guides on how to set up and customize Open Match.
|
||||
|
||||
## Precompiled container images
|
||||
|
||||
Once we reach a 1.0 release, we plan to produce publicly available (Linux) Docker container images of major releases in a public image registry. Until then, refer to the 'Compiling from source' section below.
|
||||
|
||||
## Compiling from source
|
||||
|
||||
All components of Open Match produce (Linux) Docker container images as artifacts, and there are included `Dockerfile`s for each. [Google Cloud Platform Cloud Build](https://cloud.google.com/cloud-build/docs/) users will also find `cloudbuild_COMPONENT.yaml` files for each component in the repository root.
|
||||
|
||||
All the core components for Open Match are written in Golang and use the [Dockerfile multistage builder pattern](https://docs.docker.com/develop/develop-images/multistage-build/). This pattern uses intermediate Docker containers as a Golang build environment while producing lightweight, minimized container images as final build artifacts. When the project is ready for production, we will modify the `Dockerfile`s to uncomment the last build stage. Although this pattern is great for production container images, it removes most of the utilities required to troubleshoot issues during development.
|
||||
|
||||
## Configuration
|
||||
|
||||
Currently, each component reads a local config file `matchmaker_config.json`, and all components assume they have the same configuration. To this end, there is a single centralized config file located in the `<REPO_ROOT>/config/` which is symlinked to each component's subdirectory for convenience when building locally. When `docker build`ing the component container images, the Dockerfile copies the centralized config file into the component directory.
|
||||
|
||||
We plan to replace this with a Kubernetes-managed config with dynamic reloading when development time allows. Pull requests are welcome!
|
||||
|
||||
### Guides
|
||||
* [Production guide](./docs/production.md) Lots of best practices to be written here before 1.0 release. **WIP**
|
||||
* [Development guide](./docs/development.md)
|
||||
|
||||
### Reference
|
||||
* [FAQ](./docs/faq.md)
|
||||
|
||||
## Get involved
|
||||
|
||||
* [Slack channel](https://open-match.slack.com/)
|
||||
* [Signup link](https://join.slack.com/t/open-match/shared_invite/enQtNDM1NjcxNTY4MTgzLWQzMzE1MGY5YmYyYWY3ZjE2MjNjZTdmYmQ1ZTQzMmNiNGViYmQyN2M4ZmVkMDY2YzZlOTUwMTYwMzI1Y2I2MjU)
|
||||
* [Slack Channel](https://open-match.slack.com/) ([Signup](https://join.slack.com/t/open-match/shared_invite/enQtNDM1NjcxNTY4MTgzLTM5ZWQxNjc1YWI3MzJmN2RiMWJmYWI0ZjFiNzNkZmNkMWQ3YWU5OGVkNzA5Yzc4OGVkOGU5MTc0OTA5ZTA5NDU))
|
||||
* [File an Issue](https://github.com/googleforgames/open-match/issues/new)
|
||||
* [Mailing list](https://groups.google.com/forum/#!forum/open-match-discuss)
|
||||
* [Managed Service Survey](https://goo.gl/forms/cbrFTNCmy9rItSv72)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Participation in this project comes under the [Contributor Covenant Code of Conduct](code-of-conduct.md)
|
||||
|
||||
## Development and Contribution
|
||||
|
||||
Please read the [contributing](CONTRIBUTING.md) guide for directions on submitting Pull Requests to Open Match.
|
||||
|
||||
See the [Development guide](docs/development.md) for documentation for development and building Open Match from source.
|
||||
|
||||
The [Release Process](docs/governance/release_process.md) documentation displays the project's upcoming release calendar and release process. (NYI)
|
||||
|
||||
Open Match is in active development - we would love your help in shaping its future!
|
||||
|
||||
## This all sounds great, but can you explain Docker and/or Kubernetes to me?
|
||||
|
||||
### Docker
|
||||
- [Docker's official "Getting Started" guide](https://docs.docker.com/get-started/)
|
||||
- [Katacoda's free, interactive Docker course](https://www.katacoda.com/courses/docker)
|
||||
|
||||
### Kubernetes
|
||||
- [You should totally read this comic, and interactive tutorial](https://cloud.google.com/kubernetes-engine/kubernetes-comic/)
|
||||
- [Katacoda's free, interactive Kubernetes course](https://www.katacoda.com/courses/kubernetes)
|
||||
|
||||
## Licence
|
||||
## License
|
||||
|
||||
Apache 2.0
|
||||
|
||||
# Missing functionality
|
||||
|
||||
* Player/Group records generated when a client enters the matchmaking pool need to be removed after a certain amount of time with no activity. When using Redis, this will be implemented as a expiration on the player record.
|
||||
* Instrumentation of MMFs is in the planning stages. Since MMFs are by design meant to be completely customizable (to the point of allowing any process that can be packaged in a Docker container), metrics/stats will need to have an expected format and formalized outgoing pathway. Currently the thought is that it might be that the metrics should be written to a particular key in statestorage in a format compatible with opencensus, and will be collected, aggreggated, and exported to Prometheus using another process.
|
||||
* The Kubernetes service account used by the MMFOrc should be updated to have min required permissions.
|
||||
* Autoscaling isn't turned on for the Frontend or Backend API Kubernetes deployments by default.
|
||||
* Match profiles should be able to define multiple MMF container images to run, but this is not currently supported. This enables A/B testing and several other scenarios.
|
||||
* Out-of-the-box, the Redis deployment should be a HA configuration using [Redis Sentinel](https://redis.io/topics/sentinel).
|
||||
* Redis watch should be unified to watch a hash and stream updates. The code for this is written and validated but not committed yet. We don't want to support two redis watcher code paths, so the backend watch of the match object should be switched to unify the way the frontend and backend watch keys. Unfortunately this change touches the whole chain of components that touch backend match objects (mmf, evaluator, backendapi) and so needs additional work and testing before it is integrated.
|
||||
|
||||
# Planned improvements
|
||||
|
||||
* “Writing your first matchmaker” getting started guide will be included in an upcoming version.
|
||||
* Documentation for using the example customizable components and the `backendstub` and `frontendstub` applications to do an end-to-end (e2e) test will be written. This all works now, but needs to be written up.
|
||||
* A [Helm](https://helm.sh/) chart to stand up Open Match will be provided in an upcoming version.
|
||||
* We plan to host 'official' docker images for all release versions of the core components in publicly available docker registries soon.
|
||||
* CI/CD for this repo and the associated status tags are planned.
|
||||
* Documentation on release process and release calendar.
|
||||
* [OpenCensus tracing](https://opencensus.io/core-concepts/tracing/) will be implemented in an upcoming version.
|
||||
* Read logrus logging configuration from matchmaker_config.json.
|
||||
* Golang unit tests will be shipped in an upcoming version.
|
||||
* A full load-testing and e2e testing suite will be included in an upcoming version.
|
||||
* All state storage operations should be isolated from core components into the `statestorage/` modules. This is necessary precursor work to enabling Open Match state storage to use software other than Redis.
|
||||
* The MMFOrc component name will be updated in a future version to something easier to understand. Suggestions welcome!
|
||||
* The MMFOrc component currently requires a default service account with permission to kick of k8s jobs, but the revision today makes the service account have full permissions. This needs to be reworked to have min required RBAC permissions before it is used in production, but is fine for closed testing and development.
|
||||
|
13
api/LICENSE
Normal file
13
api/LICENSE
Normal file
@ -0,0 +1,13 @@
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -1,15 +1,15 @@
|
||||
# Open Match APIs
|
||||
# Open Match API
|
||||
|
||||
This directory contains the API specification files for Open Match. API documenation will be produced in a future version, although the protobuf files offer a concise description of the API calls available, along with arguments and return messages.
|
||||
Open Match API is exposed via [gRPC](https://grpc.io/) and HTTP REST with [Swagger](https://swagger.io/tools/swagger-codegen/).
|
||||
|
||||
* [Protobuf .proto files for all APIs](./protobuf-spec/)
|
||||
gRPC has first-class support for [many languages](https://grpc.io/docs/) and provides the most performance. It is a RPC protocol built on top of HTTP/2 and provides TLS for secure transport.
|
||||
|
||||
These proto files are copied to the container image during `docker build` for the Open Match core components. The `Dockerfiles` handle the compilation for you transparently, and copy the resulting `SPEC.pb.go` files to the appropriate place in your final container image.
|
||||
For HTTP/HTTPS Open Match uses a gRPC proxy to serve the API. Since HTTP does not provide a structure for request/responses we use Swagger to provide a schema. You can view the Swagger docs for each service in this directory's `*.swagger.json` files. In addition each server will host it's swagger doc via `GET /swagger.json` if you want to dynamically load them at runtime.
|
||||
|
||||
References:
|
||||
Lastly, Open Match supports insecure and TLS mode for serving the API. It's strongly preferred to use TLS mode in production but insecure mode can be used for test and local development. To help with certificates management see `tools/certgen` to create self-signed certificates.
|
||||
|
||||
* [gRPC](https://grpc.io/)
|
||||
* [Language Guide (proto3)](https://developers.google.com/protocol-buffers/docs/proto3)
|
||||
# Open Match API Development Guide
|
||||
|
||||
Manual gRPC compilation commmand, from the directory containing the proto:
|
||||
```protoc -I . ./<filename>.proto --go_out=plugins=grpc:.```
|
||||
Open Match proto comments follow the same format as [this file](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto)
|
||||
|
||||
If you plan to change the proto definitions, please update the comments and run `make api/api.md` to reflect the latest changes in open-match-docs.
|
171
api/backend.proto
Normal file
171
api/backend.proto
Normal file
@ -0,0 +1,171 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
package openmatch;
|
||||
option go_package = "open-match.dev/open-match/pkg/pb";
|
||||
option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "Backend"
|
||||
version: "1.0"
|
||||
contact: {
|
||||
name: "Open Match"
|
||||
url: "https://open-match.dev"
|
||||
email: "open-match-discuss@googlegroups.com"
|
||||
}
|
||||
license: {
|
||||
name: "Apache 2.0 License"
|
||||
url: "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
}
|
||||
external_docs: {
|
||||
url: "https://open-match.dev/site/docs/"
|
||||
description: "Open Match Documentation"
|
||||
}
|
||||
schemes: HTTP
|
||||
schemes: HTTPS
|
||||
consumes: "application/json"
|
||||
produces: "application/json"
|
||||
responses: {
|
||||
key: "404"
|
||||
value: {
|
||||
description: "Returned when the resource does not exist."
|
||||
schema: { json_schema: { type: STRING } }
|
||||
}
|
||||
}
|
||||
// TODO Add annotations for security_defintiions.
|
||||
// See
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/proto/examplepb/a_bit_of_everything.proto
|
||||
};
|
||||
|
||||
// FunctionConfig specifies a MMF address and client type for Backend to establish connections with the MMF
|
||||
message FunctionConfig {
|
||||
string host = 1;
|
||||
int32 port = 2;
|
||||
Type type = 3;
|
||||
enum Type {
|
||||
GRPC = 0;
|
||||
REST = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message FetchMatchesRequest {
|
||||
// A configuration for the MatchFunction server of this FetchMatches call.
|
||||
FunctionConfig config = 1;
|
||||
|
||||
// A MatchProfile that will be sent to the MatchFunction server of this FetchMatches call.
|
||||
MatchProfile profile = 2;
|
||||
}
|
||||
|
||||
message FetchMatchesResponse {
|
||||
// A Match generated by the user-defined MMF with the specified MatchProfiles.
|
||||
// A valid Match response will contain at least one ticket.
|
||||
Match match = 1;
|
||||
}
|
||||
|
||||
message ReleaseTicketsRequest{
|
||||
// TicketIds is a list of string representing Open Match generated Ids to be re-enabled for MMF querying
|
||||
// because they are no longer awaiting assignment from a previous match result
|
||||
repeated string ticket_ids = 1;
|
||||
}
|
||||
|
||||
message ReleaseTicketsResponse {}
|
||||
|
||||
message ReleaseAllTicketsRequest{}
|
||||
|
||||
message ReleaseAllTicketsResponse {}
|
||||
|
||||
// AssignmentGroup contains an Assignment and the Tickets to which it should be applied.
|
||||
message AssignmentGroup{
|
||||
// TicketIds is a list of strings representing Open Match generated Ids which apply to an Assignment.
|
||||
repeated string ticket_ids = 1;
|
||||
|
||||
// An Assignment specifies game connection related information to be associated with the TicketIds.
|
||||
Assignment assignment = 2;
|
||||
}
|
||||
|
||||
// AssignmentFailure contains the id of the Ticket that failed the Assignment and the failure status.
|
||||
message AssignmentFailure {
|
||||
enum Cause {
|
||||
UNKNOWN = 0;
|
||||
TICKET_NOT_FOUND = 1;
|
||||
}
|
||||
|
||||
string ticket_id = 1;
|
||||
Cause cause = 2;
|
||||
}
|
||||
|
||||
message AssignTicketsRequest {
|
||||
// Assignments is a list of assignment groups that contain assignment and the Tickets to which they should be applied.
|
||||
repeated AssignmentGroup assignments = 1;
|
||||
}
|
||||
|
||||
message AssignTicketsResponse {
|
||||
// Failures is a list of all the Tickets that failed assignment along with the cause of failure.
|
||||
repeated AssignmentFailure failures = 1;
|
||||
}
|
||||
|
||||
// The BackendService implements APIs to generate matches and handle ticket assignments.
|
||||
service BackendService {
|
||||
// FetchMatches triggers a MatchFunction with the specified MatchProfile and
|
||||
// returns a set of matches generated by the Match Making Function, and
|
||||
// accepted by the evaluator.
|
||||
// Tickets in matches returned by FetchMatches are moved from active to
|
||||
// pending, and will not be returned by query.
|
||||
rpc FetchMatches(FetchMatchesRequest) returns (stream FetchMatchesResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/backendservice/matches:fetch"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// AssignTickets overwrites the Assignment field of the input TicketIds.
|
||||
rpc AssignTickets(AssignTicketsRequest) returns (AssignTicketsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/backendservice/tickets:assign"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// ReleaseTickets moves tickets from the pending state, to the active state.
|
||||
// This enables them to be returned by query, and find different matches.
|
||||
//
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc ReleaseTickets(ReleaseTicketsRequest) returns (ReleaseTicketsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/backendservice/tickets:release"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// ReleaseAllTickets moves all tickets from the pending state, to the active
|
||||
// state. This enables them to be returned by query, and find different
|
||||
// matches.
|
||||
//
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc ReleaseAllTickets(ReleaseAllTicketsRequest) returns (ReleaseAllTicketsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/backendservice/tickets:releaseall"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
566
api/backend.swagger.json
Normal file
566
api/backend.swagger.json
Normal file
@ -0,0 +1,566 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Backend",
|
||||
"version": "1.0",
|
||||
"contact": {
|
||||
"name": "Open Match",
|
||||
"url": "https://open-match.dev",
|
||||
"email": "open-match-discuss@googlegroups.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0 License",
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/backendservice/matches:fetch": {
|
||||
"post": {
|
||||
"summary": "FetchMatches triggers a MatchFunction with the specified MatchProfile and\nreturns a set of matches generated by the Match Making Function, and\naccepted by the evaluator.\nTickets in matches returned by FetchMatches are moved from active to\npending, and will not be returned by query.",
|
||||
"operationId": "FetchMatches",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchFetchMatchesResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchFetchMatchesRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"BackendService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/backendservice/tickets:assign": {
|
||||
"post": {
|
||||
"summary": "AssignTickets overwrites the Assignment field of the input TicketIds.",
|
||||
"operationId": "AssignTickets",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchAssignTicketsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchAssignTicketsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"BackendService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/backendservice/tickets:release": {
|
||||
"post": {
|
||||
"summary": "ReleaseTickets moves tickets from the pending state, to the active state.\nThis enables them to be returned by query, and find different matches.",
|
||||
"description": "BETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "ReleaseTickets",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchReleaseTicketsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchReleaseTicketsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"BackendService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/backendservice/tickets:releaseall": {
|
||||
"post": {
|
||||
"summary": "ReleaseAllTickets moves all tickets from the pending state, to the active\nstate. This enables them to be returned by query, and find different\nmatches.",
|
||||
"description": "BETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "ReleaseAllTickets",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchReleaseAllTicketsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchReleaseAllTicketsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"BackendService"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AssignmentFailureCause": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"UNKNOWN",
|
||||
"TICKET_NOT_FOUND"
|
||||
],
|
||||
"default": "UNKNOWN"
|
||||
},
|
||||
"openmatchAssignTicketsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"assignments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchAssignmentGroup"
|
||||
},
|
||||
"description": "Assignments is a list of assignment groups that contain assignment and the Tickets to which they should be applied."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchAssignTicketsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failures": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchAssignmentFailure"
|
||||
},
|
||||
"description": "Failures is a list of all the Tickets that failed assignment along with the cause of failure."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchAssignment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"connection": {
|
||||
"type": "string",
|
||||
"description": "Connection information for this Assignment."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchAssignmentFailure": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ticket_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"cause": {
|
||||
"$ref": "#/definitions/AssignmentFailureCause"
|
||||
}
|
||||
},
|
||||
"description": "AssignmentFailure contains the id of the Ticket that failed the Assignment and the failure status."
|
||||
},
|
||||
"openmatchAssignmentGroup": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ticket_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "TicketIds is a list of strings representing Open Match generated Ids which apply to an Assignment."
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment specifies game connection related information to be associated with the TicketIds."
|
||||
}
|
||||
},
|
||||
"description": "AssignmentGroup contains an Assignment and the Tickets to which it should be applied."
|
||||
},
|
||||
"openmatchDoubleRangeFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"double_arg": {
|
||||
"type": "string",
|
||||
"description": "Name of the ticket's search_fields.double_args this Filter operates on."
|
||||
},
|
||||
"max": {
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Maximum value."
|
||||
},
|
||||
"min": {
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Minimum value."
|
||||
}
|
||||
},
|
||||
"title": "Filters numerical values to only those within a range.\n double_arg: \"foo\"\n max: 10\n min: 5\nmatches:\n {\"foo\": 5}\n {\"foo\": 7.5}\n {\"foo\": 10}\ndoes not match:\n {\"foo\": 4}\n {\"foo\": 10.01}\n {\"foo\": \"7.5\"}\n {}"
|
||||
},
|
||||
"openmatchFetchMatchesRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {
|
||||
"$ref": "#/definitions/openmatchFunctionConfig",
|
||||
"description": "A configuration for the MatchFunction server of this FetchMatches call."
|
||||
},
|
||||
"profile": {
|
||||
"$ref": "#/definitions/openmatchMatchProfile",
|
||||
"description": "A MatchProfile that will be sent to the MatchFunction server of this FetchMatches call."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchFetchMatchesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"match": {
|
||||
"$ref": "#/definitions/openmatchMatch",
|
||||
"description": "A Match generated by the user-defined MMF with the specified MatchProfiles.\nA valid Match response will contain at least one ticket."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchFunctionConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/definitions/openmatchFunctionConfigType"
|
||||
}
|
||||
},
|
||||
"title": "FunctionConfig specifies a MMF address and client type for Backend to establish connections with the MMF"
|
||||
},
|
||||
"openmatchFunctionConfigType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"GRPC",
|
||||
"REST"
|
||||
],
|
||||
"default": "GRPC"
|
||||
},
|
||||
"openmatchMatch": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"match_id": {
|
||||
"type": "string",
|
||||
"description": "A Match ID that should be passed through the stack for tracing."
|
||||
},
|
||||
"match_profile": {
|
||||
"type": "string",
|
||||
"description": "Name of the match profile that generated this Match."
|
||||
},
|
||||
"match_function": {
|
||||
"type": "string",
|
||||
"description": "Name of the match function that generated this Match."
|
||||
},
|
||||
"tickets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchTicket"
|
||||
},
|
||||
"description": "Tickets belonging to this match."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "A Match is used to represent a completed match object. It can be generated by\na MatchFunction as a proposal or can be returned by OpenMatch as a result in\nresponse to the FetchMatches call.\nWhen a match is returned by the FetchMatches call, it should contain at least\none ticket to be considered as valid."
|
||||
},
|
||||
"openmatchMatchProfile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of this match profile."
|
||||
},
|
||||
"pools": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchPool"
|
||||
},
|
||||
"description": "Set of pools to be queried when generating a match for this MatchProfile."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "A MatchProfile is Open Match's representation of a Match specification. It is\nused to indicate the criteria for selecting players for a match. A\nMatchProfile is the input to the API to get matches and is passed to the\nMatchFunction. It contains all the information required by the MatchFunction\nto generate match proposals."
|
||||
},
|
||||
"openmatchPool": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "A developer-chosen human-readable name for this Pool."
|
||||
},
|
||||
"double_range_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchDoubleRangeFilter"
|
||||
},
|
||||
"description": "Set of Filters indicating the filtering criteria. Selected tickets must\nmatch every Filter."
|
||||
},
|
||||
"string_equals_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchStringEqualsFilter"
|
||||
}
|
||||
},
|
||||
"tag_present_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchTagPresentFilter"
|
||||
}
|
||||
},
|
||||
"created_before": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "If specified, only Tickets created before the specified time are selected."
|
||||
},
|
||||
"created_after": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "If specified, only Tickets created after the specified time are selected."
|
||||
}
|
||||
},
|
||||
"description": "Pool specfies a set of criteria that are used to select a subset of Tickets\nthat meet all the criteria."
|
||||
},
|
||||
"openmatchReleaseAllTicketsRequest": {
|
||||
"type": "object"
|
||||
},
|
||||
"openmatchReleaseAllTicketsResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"openmatchReleaseTicketsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ticket_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": "TicketIds is a list of string representing Open Match generated Ids to be re-enabled for MMF querying\nbecause they are no longer awaiting assignment from a previous match result"
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchReleaseTicketsResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"openmatchSearchFields": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"double_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"description": "Float arguments. Filterable on ranges."
|
||||
},
|
||||
"string_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "String arguments. Filterable on equality."
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filterable on presence or absence of given value."
|
||||
}
|
||||
},
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"openmatchStringEqualsFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"string_arg": {
|
||||
"type": "string",
|
||||
"description": "Name of the ticket's search_fields.string_args this Filter operates on."
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Filters strings exactly equaling a value.\n string_arg: \"foo\"\n value: \"bar\"\nmatches:\n {\"foo\": \"bar\"}\ndoes not match:\n {\"foo\": \"baz\"}\n {\"bar\": \"foo\"}\n {}"
|
||||
},
|
||||
"openmatchTagPresentFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Filters to the tag being present on the search_fields.\n tag: \"foo\"\nmatches:\n [\"foo\"]\n [\"bar\",\"foo\"]\ndoes not match:\n [\"bar\"]\n []"
|
||||
},
|
||||
"openmatchTicket": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type_url": {
|
||||
"type": "string",
|
||||
"description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics."
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "Must be a valid serialized protocol buffer of the above specified type."
|
||||
}
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchFetchMatchesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchFetchMatchesResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchFetchMatchesResponse"
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Open Match Documentation",
|
||||
"url": "https://open-match.dev/site/docs/"
|
||||
}
|
||||
}
|
80
api/evaluator.proto
Normal file
80
api/evaluator.proto
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
package openmatch;
|
||||
option go_package = "open-match.dev/open-match/pkg/pb";
|
||||
option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "Evaluator"
|
||||
version: "1.0"
|
||||
contact: {
|
||||
name: "Open Match"
|
||||
url: "https://open-match.dev"
|
||||
email: "open-match-discuss@googlegroups.com"
|
||||
}
|
||||
license: {
|
||||
name: "Apache 2.0 License"
|
||||
url: "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
}
|
||||
external_docs: {
|
||||
url: "https://open-match.dev/site/docs/"
|
||||
description: "Open Match Documentation"
|
||||
}
|
||||
schemes: HTTP
|
||||
schemes: HTTPS
|
||||
consumes: "application/json"
|
||||
produces: "application/json"
|
||||
responses: {
|
||||
key: "404"
|
||||
value: {
|
||||
description: "Returned when the resource does not exist."
|
||||
schema: { json_schema: { type: STRING } }
|
||||
}
|
||||
}
|
||||
// TODO Add annotations for security_defintiions.
|
||||
// See
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/proto/examplepb/a_bit_of_everything.proto
|
||||
};
|
||||
|
||||
message EvaluateRequest {
|
||||
// A Matches proposed by the Match Function representing a candidate of the final results.
|
||||
Match match = 1;
|
||||
}
|
||||
|
||||
message EvaluateResponse {
|
||||
// A Match ID representing a shortlisted match returned by the evaluator as the final result.
|
||||
string match_id = 2;
|
||||
|
||||
// Deprecated fields
|
||||
reserved 1;
|
||||
}
|
||||
|
||||
// The Evaluator service implements APIs used to evaluate and shortlist matches proposed by MMFs.
|
||||
service Evaluator {
|
||||
// Evaluate evaluates a list of proposed matches based on quality, collision status, and etc, then shortlist the matches and returns the final results.
|
||||
rpc Evaluate(stream EvaluateRequest) returns (stream EvaluateResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/evaluator/matches:evaluate"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
248
api/evaluator.swagger.json
Normal file
248
api/evaluator.swagger.json
Normal file
@ -0,0 +1,248 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Evaluator",
|
||||
"version": "1.0",
|
||||
"contact": {
|
||||
"name": "Open Match",
|
||||
"url": "https://open-match.dev",
|
||||
"email": "open-match-discuss@googlegroups.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0 License",
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/evaluator/matches:evaluate": {
|
||||
"post": {
|
||||
"summary": "Evaluate evaluates a list of proposed matches based on quality, collision status, and etc, then shortlist the matches and returns the final results.",
|
||||
"operationId": "Evaluate",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchEvaluateResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"description": " (streaming inputs)",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchEvaluateRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Evaluator"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"openmatchAssignment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"connection": {
|
||||
"type": "string",
|
||||
"description": "Connection information for this Assignment."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchEvaluateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"match": {
|
||||
"$ref": "#/definitions/openmatchMatch",
|
||||
"description": "A Matches proposed by the Match Function representing a candidate of the final results."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchEvaluateResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"match_id": {
|
||||
"type": "string",
|
||||
"description": "A Match ID representing a shortlisted match returned by the evaluator as the final result."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchMatch": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"match_id": {
|
||||
"type": "string",
|
||||
"description": "A Match ID that should be passed through the stack for tracing."
|
||||
},
|
||||
"match_profile": {
|
||||
"type": "string",
|
||||
"description": "Name of the match profile that generated this Match."
|
||||
},
|
||||
"match_function": {
|
||||
"type": "string",
|
||||
"description": "Name of the match function that generated this Match."
|
||||
},
|
||||
"tickets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchTicket"
|
||||
},
|
||||
"description": "Tickets belonging to this match."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "A Match is used to represent a completed match object. It can be generated by\na MatchFunction as a proposal or can be returned by OpenMatch as a result in\nresponse to the FetchMatches call.\nWhen a match is returned by the FetchMatches call, it should contain at least\none ticket to be considered as valid."
|
||||
},
|
||||
"openmatchSearchFields": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"double_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"description": "Float arguments. Filterable on ranges."
|
||||
},
|
||||
"string_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "String arguments. Filterable on equality."
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filterable on presence or absence of given value."
|
||||
}
|
||||
},
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"openmatchTicket": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type_url": {
|
||||
"type": "string",
|
||||
"description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics."
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "Must be a valid serialized protocol buffer of the above specified type."
|
||||
}
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchEvaluateResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchEvaluateResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchEvaluateResponse"
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Open Match Documentation",
|
||||
"url": "https://open-match.dev/site/docs/"
|
||||
}
|
||||
}
|
24
api/extensions.proto
Normal file
24
api/extensions.proto
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
package openmatch;
|
||||
option go_package = "open-match.dev/open-match/pkg/pb";
|
||||
option csharp_namespace = "OpenMatch";
|
||||
|
||||
// A DefaultEvaluationCriteria is used for a match's evaluation_input when using
|
||||
// the default evaluator.
|
||||
message DefaultEvaluationCriteria {
|
||||
double score = 1;
|
||||
}
|
120
api/frontend.proto
Normal file
120
api/frontend.proto
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
package openmatch;
|
||||
option go_package = "open-match.dev/open-match/pkg/pb";
|
||||
option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "Frontend"
|
||||
version: "1.0"
|
||||
contact: {
|
||||
name: "Open Match"
|
||||
url: "https://open-match.dev"
|
||||
email: "open-match-discuss@googlegroups.com"
|
||||
}
|
||||
license: {
|
||||
name: "Apache 2.0 License"
|
||||
url: "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
}
|
||||
external_docs: {
|
||||
url: "https://open-match.dev/site/docs/"
|
||||
description: "Open Match Documentation"
|
||||
}
|
||||
schemes: HTTP
|
||||
schemes: HTTPS
|
||||
consumes: "application/json"
|
||||
produces: "application/json"
|
||||
responses: {
|
||||
key: "404"
|
||||
value: {
|
||||
description: "Returned when the resource does not exist."
|
||||
schema: { json_schema: { type: STRING } }
|
||||
}
|
||||
}
|
||||
// TODO Add annotations for security_defintiions.
|
||||
// See
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/proto/examplepb/a_bit_of_everything.proto
|
||||
};
|
||||
|
||||
message CreateTicketRequest {
|
||||
// A Ticket object with SearchFields defined.
|
||||
Ticket ticket = 1;
|
||||
}
|
||||
|
||||
message DeleteTicketRequest {
|
||||
// A TicketId of a generated Ticket to be deleted.
|
||||
string ticket_id = 1;
|
||||
}
|
||||
|
||||
message GetTicketRequest {
|
||||
// A TicketId of a generated Ticket.
|
||||
string ticket_id = 1;
|
||||
}
|
||||
|
||||
message WatchAssignmentsRequest {
|
||||
// A TicketId of a generated Ticket to get updates on.
|
||||
string ticket_id = 1;
|
||||
}
|
||||
|
||||
message WatchAssignmentsResponse {
|
||||
// An updated Assignment of the requested Ticket.
|
||||
Assignment assignment = 1;
|
||||
}
|
||||
|
||||
// The FrontendService implements APIs to manage and query status of a Tickets.
|
||||
service FrontendService {
|
||||
// CreateTicket assigns an unique TicketId to the input Ticket and record it in state storage.
|
||||
// A ticket is considered as ready for matchmaking once it is created.
|
||||
// - If a TicketId exists in a Ticket request, an auto-generated TicketId will override this field.
|
||||
// - If SearchFields exist in a Ticket, CreateTicket will also index these fields such that one can query the ticket with query.QueryTickets function.
|
||||
rpc CreateTicket(CreateTicketRequest) returns (Ticket) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/frontendservice/tickets"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// DeleteTicket immediately stops Open Match from using the Ticket for matchmaking and removes the Ticket from state storage.
|
||||
// The client should delete the Ticket when finished matchmaking with it.
|
||||
rpc DeleteTicket(DeleteTicketRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {
|
||||
delete: "/v1/frontendservice/tickets/{ticket_id}"
|
||||
};
|
||||
}
|
||||
|
||||
// GetTicket get the Ticket associated with the specified TicketId.
|
||||
rpc GetTicket(GetTicketRequest) returns (Ticket) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/frontendservice/tickets/{ticket_id}"
|
||||
};
|
||||
}
|
||||
|
||||
// WatchAssignments stream back Assignment of the specified TicketId if it is updated.
|
||||
// - If the Assignment is not updated, GetAssignment will retry using the configured backoff strategy.
|
||||
rpc WatchAssignments(WatchAssignmentsRequest)
|
||||
returns (stream WatchAssignmentsResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/frontendservice/tickets/{ticket_id}/assignments"
|
||||
};
|
||||
}
|
||||
}
|
312
api/frontend.swagger.json
Normal file
312
api/frontend.swagger.json
Normal file
@ -0,0 +1,312 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Frontend",
|
||||
"version": "1.0",
|
||||
"contact": {
|
||||
"name": "Open Match",
|
||||
"url": "https://open-match.dev",
|
||||
"email": "open-match-discuss@googlegroups.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0 License",
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/frontendservice/tickets": {
|
||||
"post": {
|
||||
"summary": "CreateTicket assigns an unique TicketId to the input Ticket and record it in state storage.\nA ticket is considered as ready for matchmaking once it is created.\n - If a TicketId exists in a Ticket request, an auto-generated TicketId will override this field.\n - If SearchFields exist in a Ticket, CreateTicket will also index these fields such that one can query the ticket with query.QueryTickets function.",
|
||||
"operationId": "CreateTicket",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchTicket"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchCreateTicketRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"FrontendService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/frontendservice/tickets/{ticket_id}": {
|
||||
"get": {
|
||||
"summary": "GetTicket get the Ticket associated with the specified TicketId.",
|
||||
"operationId": "GetTicket",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchTicket"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "ticket_id",
|
||||
"description": "A TicketId of a generated Ticket.",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"FrontendService"
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"summary": "DeleteTicket immediately stops Open Match from using the Ticket for matchmaking and removes the Ticket from state storage.\nThe client should delete the Ticket when finished matchmaking with it.",
|
||||
"operationId": "DeleteTicket",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"properties": {}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "ticket_id",
|
||||
"description": "A TicketId of a generated Ticket to be deleted.",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"FrontendService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/frontendservice/tickets/{ticket_id}/assignments": {
|
||||
"get": {
|
||||
"summary": "WatchAssignments stream back Assignment of the specified TicketId if it is updated.\n - If the Assignment is not updated, GetAssignment will retry using the configured backoff strategy.",
|
||||
"operationId": "WatchAssignments",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchWatchAssignmentsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "ticket_id",
|
||||
"description": "A TicketId of a generated Ticket to get updates on.",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"FrontendService"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"openmatchAssignment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"connection": {
|
||||
"type": "string",
|
||||
"description": "Connection information for this Assignment."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchCreateTicketRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ticket": {
|
||||
"$ref": "#/definitions/openmatchTicket",
|
||||
"description": "A Ticket object with SearchFields defined."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchSearchFields": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"double_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"description": "Float arguments. Filterable on ranges."
|
||||
},
|
||||
"string_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "String arguments. Filterable on equality."
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filterable on presence or absence of given value."
|
||||
}
|
||||
},
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"openmatchTicket": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"openmatchWatchAssignmentsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An updated Assignment of the requested Ticket."
|
||||
}
|
||||
}
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type_url": {
|
||||
"type": "string",
|
||||
"description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics."
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "Must be a valid serialized protocol buffer of the above specified type."
|
||||
}
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchWatchAssignmentsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchWatchAssignmentsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchWatchAssignmentsResponse"
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Open Match Documentation",
|
||||
"url": "https://open-match.dev/site/docs/"
|
||||
}
|
||||
}
|
80
api/matchfunction.proto
Normal file
80
api/matchfunction.proto
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
package openmatch;
|
||||
option go_package = "open-match.dev/open-match/pkg/pb";
|
||||
option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "Match Function"
|
||||
version: "1.0"
|
||||
contact: {
|
||||
name: "Open Match"
|
||||
url: "https://open-match.dev"
|
||||
email: "open-match-discuss@googlegroups.com"
|
||||
}
|
||||
license: {
|
||||
name: "Apache 2.0 License"
|
||||
url: "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
}
|
||||
external_docs: {
|
||||
url: "https://open-match.dev/site/docs/"
|
||||
description: "Open Match Documentation"
|
||||
}
|
||||
schemes: HTTP
|
||||
schemes: HTTPS
|
||||
consumes: "application/json"
|
||||
produces: "application/json"
|
||||
responses: {
|
||||
key: "404"
|
||||
value: {
|
||||
description: "Returned when the resource does not exist."
|
||||
schema: { json_schema: { type: STRING } }
|
||||
}
|
||||
}
|
||||
// TODO Add annotations for security_defintiions.
|
||||
// See
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/proto/examplepb/a_bit_of_everything.proto
|
||||
};
|
||||
|
||||
message RunRequest {
|
||||
// A MatchProfile defines constraints of Tickets in a Match and shapes the Match proposed by the MatchFunction.
|
||||
MatchProfile profile = 1;
|
||||
}
|
||||
|
||||
message RunResponse {
|
||||
// A Proposal represents a Match candidate that satifies the constraints defined in the input Profile.
|
||||
// A valid Proposal response will contain at least one ticket.
|
||||
Match proposal = 1;
|
||||
}
|
||||
|
||||
// The MatchFunction service implements APIs to run user-defined matchmaking logics.
|
||||
service MatchFunction {
|
||||
// DO NOT CALL THIS FUNCTION MANUALLY. USE backend.FetchMatches INSTEAD.
|
||||
// Run pulls Tickets that satisfy Profile constraints from QueryService, runs matchmaking logics against them, then
|
||||
// constructs and streams back match candidates to the Backend service.
|
||||
rpc Run(RunRequest) returns (stream RunResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/matchfunction:run"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
352
api/matchfunction.swagger.json
Normal file
352
api/matchfunction.swagger.json
Normal file
@ -0,0 +1,352 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Match Function",
|
||||
"version": "1.0",
|
||||
"contact": {
|
||||
"name": "Open Match",
|
||||
"url": "https://open-match.dev",
|
||||
"email": "open-match-discuss@googlegroups.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0 License",
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/matchfunction:run": {
|
||||
"post": {
|
||||
"summary": "DO NOT CALL THIS FUNCTION MANUALLY. USE backend.FetchMatches INSTEAD.\nRun pulls Tickets that satisfy Profile constraints from QueryService, runs matchmaking logics against them, then\nconstructs and streams back match candidates to the Backend service.",
|
||||
"operationId": "Run",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchRunResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchRunRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"MatchFunction"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"openmatchAssignment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"connection": {
|
||||
"type": "string",
|
||||
"description": "Connection information for this Assignment."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchDoubleRangeFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"double_arg": {
|
||||
"type": "string",
|
||||
"description": "Name of the ticket's search_fields.double_args this Filter operates on."
|
||||
},
|
||||
"max": {
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Maximum value."
|
||||
},
|
||||
"min": {
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Minimum value."
|
||||
}
|
||||
},
|
||||
"title": "Filters numerical values to only those within a range.\n double_arg: \"foo\"\n max: 10\n min: 5\nmatches:\n {\"foo\": 5}\n {\"foo\": 7.5}\n {\"foo\": 10}\ndoes not match:\n {\"foo\": 4}\n {\"foo\": 10.01}\n {\"foo\": \"7.5\"}\n {}"
|
||||
},
|
||||
"openmatchMatch": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"match_id": {
|
||||
"type": "string",
|
||||
"description": "A Match ID that should be passed through the stack for tracing."
|
||||
},
|
||||
"match_profile": {
|
||||
"type": "string",
|
||||
"description": "Name of the match profile that generated this Match."
|
||||
},
|
||||
"match_function": {
|
||||
"type": "string",
|
||||
"description": "Name of the match function that generated this Match."
|
||||
},
|
||||
"tickets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchTicket"
|
||||
},
|
||||
"description": "Tickets belonging to this match."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "A Match is used to represent a completed match object. It can be generated by\na MatchFunction as a proposal or can be returned by OpenMatch as a result in\nresponse to the FetchMatches call.\nWhen a match is returned by the FetchMatches call, it should contain at least\none ticket to be considered as valid."
|
||||
},
|
||||
"openmatchMatchProfile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of this match profile."
|
||||
},
|
||||
"pools": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchPool"
|
||||
},
|
||||
"description": "Set of pools to be queried when generating a match for this MatchProfile."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "A MatchProfile is Open Match's representation of a Match specification. It is\nused to indicate the criteria for selecting players for a match. A\nMatchProfile is the input to the API to get matches and is passed to the\nMatchFunction. It contains all the information required by the MatchFunction\nto generate match proposals."
|
||||
},
|
||||
"openmatchPool": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "A developer-chosen human-readable name for this Pool."
|
||||
},
|
||||
"double_range_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchDoubleRangeFilter"
|
||||
},
|
||||
"description": "Set of Filters indicating the filtering criteria. Selected tickets must\nmatch every Filter."
|
||||
},
|
||||
"string_equals_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchStringEqualsFilter"
|
||||
}
|
||||
},
|
||||
"tag_present_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchTagPresentFilter"
|
||||
}
|
||||
},
|
||||
"created_before": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "If specified, only Tickets created before the specified time are selected."
|
||||
},
|
||||
"created_after": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "If specified, only Tickets created after the specified time are selected."
|
||||
}
|
||||
},
|
||||
"description": "Pool specfies a set of criteria that are used to select a subset of Tickets\nthat meet all the criteria."
|
||||
},
|
||||
"openmatchRunRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"profile": {
|
||||
"$ref": "#/definitions/openmatchMatchProfile",
|
||||
"description": "A MatchProfile defines constraints of Tickets in a Match and shapes the Match proposed by the MatchFunction."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchRunResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"proposal": {
|
||||
"$ref": "#/definitions/openmatchMatch",
|
||||
"description": "A Proposal represents a Match candidate that satifies the constraints defined in the input Profile.\nA valid Proposal response will contain at least one ticket."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchSearchFields": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"double_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"description": "Float arguments. Filterable on ranges."
|
||||
},
|
||||
"string_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "String arguments. Filterable on equality."
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filterable on presence or absence of given value."
|
||||
}
|
||||
},
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"openmatchStringEqualsFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"string_arg": {
|
||||
"type": "string",
|
||||
"description": "Name of the ticket's search_fields.string_args this Filter operates on."
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Filters strings exactly equaling a value.\n string_arg: \"foo\"\n value: \"bar\"\nmatches:\n {\"foo\": \"bar\"}\ndoes not match:\n {\"foo\": \"baz\"}\n {\"bar\": \"foo\"}\n {}"
|
||||
},
|
||||
"openmatchTagPresentFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Filters to the tag being present on the search_fields.\n tag: \"foo\"\nmatches:\n [\"foo\"]\n [\"bar\",\"foo\"]\ndoes not match:\n [\"bar\"]\n []"
|
||||
},
|
||||
"openmatchTicket": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type_url": {
|
||||
"type": "string",
|
||||
"description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics."
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "Must be a valid serialized protocol buffer of the above specified type."
|
||||
}
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchRunResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchRunResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchRunResponse"
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Open Match Documentation",
|
||||
"url": "https://open-match.dev/site/docs/"
|
||||
}
|
||||
}
|
206
api/messages.proto
Normal file
206
api/messages.proto
Normal file
@ -0,0 +1,206 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
package openmatch;
|
||||
option go_package = "open-match.dev/open-match/pkg/pb";
|
||||
option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "google/rpc/status.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent
|
||||
// an individual 'Player', a 'Group' of players, or any other concepts unique to
|
||||
// your use case. Open Match will not interpret what the Ticket represents but
|
||||
// just treat it as a matchmaking unit with a set of SearchFields. Open Match
|
||||
// stores the Ticket in state storage and enables an Assignment to be set on the
|
||||
// Ticket.
|
||||
message Ticket {
|
||||
// Id represents an auto-generated Id issued by Open Match.
|
||||
string id = 1;
|
||||
|
||||
// An Assignment represents a game server assignment associated with a Ticket,
|
||||
// or whatever finalized matched state means for your use case.
|
||||
// Open Match does not require or inspect any fields on Assignment.
|
||||
Assignment assignment = 3;
|
||||
|
||||
// Search fields are the fields which Open Match is aware of, and can be used
|
||||
// when specifying filters.
|
||||
SearchFields search_fields = 4;
|
||||
|
||||
// Customized information not inspected by Open Match, to be used by the match
|
||||
// making function, evaluator, and components making calls to Open Match.
|
||||
// Optional, depending on the requirements of the connected systems.
|
||||
map<string, google.protobuf.Any> extensions = 5;
|
||||
|
||||
// Create time is the time the Ticket was created. It is populated by Open
|
||||
// Match at the time of Ticket creation.
|
||||
google.protobuf.Timestamp create_time = 6;
|
||||
|
||||
// Deprecated fields.
|
||||
reserved 2;
|
||||
}
|
||||
|
||||
// Search fields are the fields which Open Match is aware of, and can be used
|
||||
// when specifying filters.
|
||||
message SearchFields {
|
||||
// Float arguments. Filterable on ranges.
|
||||
map<string, double> double_args = 1;
|
||||
|
||||
// String arguments. Filterable on equality.
|
||||
map<string, string> string_args = 2;
|
||||
|
||||
// Filterable on presence or absence of given value.
|
||||
repeated string tags = 3;
|
||||
}
|
||||
|
||||
// An Assignment represents a game server assignment associated with a Ticket.
|
||||
// Open Match does not require or inspect any fields on assignment.
|
||||
message Assignment {
|
||||
// Connection information for this Assignment.
|
||||
string connection = 1;
|
||||
|
||||
// Customized information not inspected by Open Match, to be used by the match
|
||||
// making function, evaluator, and components making calls to Open Match.
|
||||
// Optional, depending on the requirements of the connected systems.
|
||||
map<string, google.protobuf.Any> extensions = 4;
|
||||
|
||||
// Deprecated fields.
|
||||
reserved 2, 3;
|
||||
}
|
||||
|
||||
// Filters numerical values to only those within a range.
|
||||
// double_arg: "foo"
|
||||
// max: 10
|
||||
// min: 5
|
||||
// matches:
|
||||
// {"foo": 5}
|
||||
// {"foo": 7.5}
|
||||
// {"foo": 10}
|
||||
// does not match:
|
||||
// {"foo": 4}
|
||||
// {"foo": 10.01}
|
||||
// {"foo": "7.5"}
|
||||
// {}
|
||||
message DoubleRangeFilter {
|
||||
// Name of the ticket's search_fields.double_args this Filter operates on.
|
||||
string double_arg = 1;
|
||||
|
||||
// Maximum value.
|
||||
double max = 2;
|
||||
|
||||
// Minimum value.
|
||||
double min = 3;
|
||||
}
|
||||
|
||||
// Filters strings exactly equaling a value.
|
||||
// string_arg: "foo"
|
||||
// value: "bar"
|
||||
// matches:
|
||||
// {"foo": "bar"}
|
||||
// does not match:
|
||||
// {"foo": "baz"}
|
||||
// {"bar": "foo"}
|
||||
// {}
|
||||
message StringEqualsFilter {
|
||||
// Name of the ticket's search_fields.string_args this Filter operates on.
|
||||
string string_arg = 1;
|
||||
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
// Filters to the tag being present on the search_fields.
|
||||
// tag: "foo"
|
||||
// matches:
|
||||
// ["foo"]
|
||||
// ["bar","foo"]
|
||||
// does not match:
|
||||
// ["bar"]
|
||||
// []
|
||||
message TagPresentFilter {
|
||||
string tag = 1;
|
||||
}
|
||||
|
||||
// Pool specfies a set of criteria that are used to select a subset of Tickets
|
||||
// that meet all the criteria.
|
||||
message Pool {
|
||||
// A developer-chosen human-readable name for this Pool.
|
||||
string name = 1;
|
||||
|
||||
// Set of Filters indicating the filtering criteria. Selected tickets must
|
||||
// match every Filter.
|
||||
repeated DoubleRangeFilter double_range_filters = 2;
|
||||
|
||||
repeated StringEqualsFilter string_equals_filters = 4;
|
||||
|
||||
repeated TagPresentFilter tag_present_filters = 5;
|
||||
|
||||
// If specified, only Tickets created before the specified time are selected.
|
||||
google.protobuf.Timestamp created_before = 6;
|
||||
|
||||
// If specified, only Tickets created after the specified time are selected.
|
||||
google.protobuf.Timestamp created_after = 7;
|
||||
|
||||
// Deprecated fields.
|
||||
reserved 3;
|
||||
}
|
||||
|
||||
// A MatchProfile is Open Match's representation of a Match specification. It is
|
||||
// used to indicate the criteria for selecting players for a match. A
|
||||
// MatchProfile is the input to the API to get matches and is passed to the
|
||||
// MatchFunction. It contains all the information required by the MatchFunction
|
||||
// to generate match proposals.
|
||||
message MatchProfile {
|
||||
// Name of this match profile.
|
||||
string name = 1;
|
||||
|
||||
// Set of pools to be queried when generating a match for this MatchProfile.
|
||||
repeated Pool pools = 3;
|
||||
|
||||
// Customized information not inspected by Open Match, to be used by the match
|
||||
// making function, evaluator, and components making calls to Open Match.
|
||||
// Optional, depending on the requirements of the connected systems.
|
||||
map<string, google.protobuf.Any> extensions = 5;
|
||||
|
||||
// Deprecated fields.
|
||||
reserved 2, 4;
|
||||
}
|
||||
|
||||
// A Match is used to represent a completed match object. It can be generated by
|
||||
// a MatchFunction as a proposal or can be returned by OpenMatch as a result in
|
||||
// response to the FetchMatches call.
|
||||
// When a match is returned by the FetchMatches call, it should contain at least
|
||||
// one ticket to be considered as valid.
|
||||
message Match {
|
||||
// A Match ID that should be passed through the stack for tracing.
|
||||
string match_id = 1;
|
||||
|
||||
// Name of the match profile that generated this Match.
|
||||
string match_profile = 2;
|
||||
|
||||
// Name of the match function that generated this Match.
|
||||
string match_function = 3;
|
||||
|
||||
// Tickets belonging to this match.
|
||||
repeated Ticket tickets = 4;
|
||||
|
||||
// Customized information not inspected by Open Match, to be used by the match
|
||||
// making function, evaluator, and components making calls to Open Match.
|
||||
// Optional, depending on the requirements of the connected systems.
|
||||
map<string, google.protobuf.Any> extensions = 7;
|
||||
|
||||
// Deprecated fields.
|
||||
reserved 5, 6;
|
||||
}
|
@ -1,482 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: backend.proto
|
||||
|
||||
/*
|
||||
Package backend is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
backend.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Profile
|
||||
MatchObject
|
||||
Result
|
||||
Roster
|
||||
ConnectionInfo
|
||||
Assignments
|
||||
*/
|
||||
package backend
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Data structure for a profile to pass to the matchmaking function.
|
||||
type Profile struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Properties string `protobuf:"bytes,2,opt,name=properties" json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Profile) Reset() { *m = Profile{} }
|
||||
func (m *Profile) String() string { return proto.CompactTextString(m) }
|
||||
func (*Profile) ProtoMessage() {}
|
||||
func (*Profile) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *Profile) GetId() string {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Profile) GetProperties() string {
|
||||
if m != nil {
|
||||
return m.Properties
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Data structure for all the properties of a match.
|
||||
type MatchObject struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Properties string `protobuf:"bytes,2,opt,name=properties" json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
func (m *MatchObject) Reset() { *m = MatchObject{} }
|
||||
func (m *MatchObject) String() string { return proto.CompactTextString(m) }
|
||||
func (*MatchObject) ProtoMessage() {}
|
||||
func (*MatchObject) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *MatchObject) GetId() string {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *MatchObject) GetProperties() string {
|
||||
if m != nil {
|
||||
return m.Properties
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Simple message to return success/failure and error status.
|
||||
type Result struct {
|
||||
Success bool `protobuf:"varint,1,opt,name=success" json:"success,omitempty"`
|
||||
Error string `protobuf:"bytes,2,opt,name=error" json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Result) Reset() { *m = Result{} }
|
||||
func (m *Result) String() string { return proto.CompactTextString(m) }
|
||||
func (*Result) ProtoMessage() {}
|
||||
func (*Result) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func (m *Result) GetSuccess() bool {
|
||||
if m != nil {
|
||||
return m.Success
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Result) GetError() string {
|
||||
if m != nil {
|
||||
return m.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Data structure to hold a list of players in a match.
|
||||
type Roster struct {
|
||||
PlayerIds string `protobuf:"bytes,1,opt,name=player_ids,json=playerIds" json:"player_ids,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Roster) Reset() { *m = Roster{} }
|
||||
func (m *Roster) String() string { return proto.CompactTextString(m) }
|
||||
func (*Roster) ProtoMessage() {}
|
||||
func (*Roster) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
func (m *Roster) GetPlayerIds() string {
|
||||
if m != nil {
|
||||
return m.PlayerIds
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Simple message used to pass the connection string for the DGS to the player.
|
||||
type ConnectionInfo struct {
|
||||
ConnectionString string `protobuf:"bytes,1,opt,name=connection_string,json=connectionString" json:"connection_string,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ConnectionInfo) Reset() { *m = ConnectionInfo{} }
|
||||
func (m *ConnectionInfo) String() string { return proto.CompactTextString(m) }
|
||||
func (*ConnectionInfo) ProtoMessage() {}
|
||||
func (*ConnectionInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
|
||||
func (m *ConnectionInfo) GetConnectionString() string {
|
||||
if m != nil {
|
||||
return m.ConnectionString
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Assignments struct {
|
||||
Roster *Roster `protobuf:"bytes,1,opt,name=roster" json:"roster,omitempty"`
|
||||
ConnectionInfo *ConnectionInfo `protobuf:"bytes,2,opt,name=connection_info,json=connectionInfo" json:"connection_info,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Assignments) Reset() { *m = Assignments{} }
|
||||
func (m *Assignments) String() string { return proto.CompactTextString(m) }
|
||||
func (*Assignments) ProtoMessage() {}
|
||||
func (*Assignments) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||
|
||||
func (m *Assignments) GetRoster() *Roster {
|
||||
if m != nil {
|
||||
return m.Roster
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Assignments) GetConnectionInfo() *ConnectionInfo {
|
||||
if m != nil {
|
||||
return m.ConnectionInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Profile)(nil), "Profile")
|
||||
proto.RegisterType((*MatchObject)(nil), "MatchObject")
|
||||
proto.RegisterType((*Result)(nil), "Result")
|
||||
proto.RegisterType((*Roster)(nil), "Roster")
|
||||
proto.RegisterType((*ConnectionInfo)(nil), "ConnectionInfo")
|
||||
proto.RegisterType((*Assignments)(nil), "Assignments")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for API service
|
||||
|
||||
type APIClient interface {
|
||||
// Calls to ask the matchmaker to run a matchmaking function.
|
||||
//
|
||||
// Run MMF once. Return a matchobject that fits this profile.
|
||||
CreateMatch(ctx context.Context, in *Profile, opts ...grpc.CallOption) (*MatchObject, error)
|
||||
// Continually run MMF and stream matchobjects that fit this profile until
|
||||
// client closes the connection.
|
||||
ListMatches(ctx context.Context, in *Profile, opts ...grpc.CallOption) (API_ListMatchesClient, error)
|
||||
// Delete a matchobject from state storage manually. (Matchobjects in state
|
||||
// storage will also automatically expire after a while)
|
||||
DeleteMatch(ctx context.Context, in *MatchObject, opts ...grpc.CallOption) (*Result, error)
|
||||
// Call that manage communication of DGS connection info to players.
|
||||
//
|
||||
// Write the DGS connection info for the list of players in the
|
||||
// Assignments.roster to state storage, so that info can be read by the game
|
||||
// client(s).
|
||||
CreateAssignments(ctx context.Context, in *Assignments, opts ...grpc.CallOption) (*Result, error)
|
||||
// Remove DGS connection info for the list of players in the Roster from
|
||||
// state storage.
|
||||
DeleteAssignments(ctx context.Context, in *Roster, opts ...grpc.CallOption) (*Result, error)
|
||||
}
|
||||
|
||||
type aPIClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewAPIClient(cc *grpc.ClientConn) APIClient {
|
||||
return &aPIClient{cc}
|
||||
}
|
||||
|
||||
func (c *aPIClient) CreateMatch(ctx context.Context, in *Profile, opts ...grpc.CallOption) (*MatchObject, error) {
|
||||
out := new(MatchObject)
|
||||
err := grpc.Invoke(ctx, "/API/CreateMatch", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) ListMatches(ctx context.Context, in *Profile, opts ...grpc.CallOption) (API_ListMatchesClient, error) {
|
||||
stream, err := grpc.NewClientStream(ctx, &_API_serviceDesc.Streams[0], c.cc, "/API/ListMatches", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &aPIListMatchesClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type API_ListMatchesClient interface {
|
||||
Recv() (*MatchObject, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type aPIListMatchesClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *aPIListMatchesClient) Recv() (*MatchObject, error) {
|
||||
m := new(MatchObject)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) DeleteMatch(ctx context.Context, in *MatchObject, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/DeleteMatch", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) CreateAssignments(ctx context.Context, in *Assignments, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/CreateAssignments", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) DeleteAssignments(ctx context.Context, in *Roster, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/DeleteAssignments", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for API service
|
||||
|
||||
type APIServer interface {
|
||||
// Calls to ask the matchmaker to run a matchmaking function.
|
||||
//
|
||||
// Run MMF once. Return a matchobject that fits this profile.
|
||||
CreateMatch(context.Context, *Profile) (*MatchObject, error)
|
||||
// Continually run MMF and stream matchobjects that fit this profile until
|
||||
// client closes the connection.
|
||||
ListMatches(*Profile, API_ListMatchesServer) error
|
||||
// Delete a matchobject from state storage manually. (Matchobjects in state
|
||||
// storage will also automatically expire after a while)
|
||||
DeleteMatch(context.Context, *MatchObject) (*Result, error)
|
||||
// Call that manage communication of DGS connection info to players.
|
||||
//
|
||||
// Write the DGS connection info for the list of players in the
|
||||
// Assignments.roster to state storage, so that info can be read by the game
|
||||
// client(s).
|
||||
CreateAssignments(context.Context, *Assignments) (*Result, error)
|
||||
// Remove DGS connection info for the list of players in the Roster from
|
||||
// state storage.
|
||||
DeleteAssignments(context.Context, *Roster) (*Result, error)
|
||||
}
|
||||
|
||||
func RegisterAPIServer(s *grpc.Server, srv APIServer) {
|
||||
s.RegisterService(&_API_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _API_CreateMatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Profile)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).CreateMatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/CreateMatch",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).CreateMatch(ctx, req.(*Profile))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_ListMatches_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(Profile)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(APIServer).ListMatches(m, &aPIListMatchesServer{stream})
|
||||
}
|
||||
|
||||
type API_ListMatchesServer interface {
|
||||
Send(*MatchObject) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type aPIListMatchesServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *aPIListMatchesServer) Send(m *MatchObject) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _API_DeleteMatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MatchObject)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).DeleteMatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/DeleteMatch",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).DeleteMatch(ctx, req.(*MatchObject))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_CreateAssignments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Assignments)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).CreateAssignments(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/CreateAssignments",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).CreateAssignments(ctx, req.(*Assignments))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_DeleteAssignments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Roster)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).DeleteAssignments(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/DeleteAssignments",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).DeleteAssignments(ctx, req.(*Roster))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _API_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "API",
|
||||
HandlerType: (*APIServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "CreateMatch",
|
||||
Handler: _API_CreateMatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteMatch",
|
||||
Handler: _API_DeleteMatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateAssignments",
|
||||
Handler: _API_CreateAssignments_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteAssignments",
|
||||
Handler: _API_DeleteAssignments_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "ListMatches",
|
||||
Handler: _API_ListMatches_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "backend.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("backend.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 344 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xcf, 0x4e, 0xc2, 0x40,
|
||||
0x10, 0xc6, 0x29, 0xc6, 0x16, 0x66, 0x11, 0x64, 0xe3, 0x81, 0x90, 0xf8, 0x27, 0x3d, 0x88, 0x46,
|
||||
0xb3, 0x31, 0x78, 0xc1, 0x83, 0x07, 0x82, 0x17, 0x12, 0x8d, 0xa4, 0x3e, 0x00, 0x29, 0xdb, 0x01,
|
||||
0x56, 0xeb, 0x6e, 0xb3, 0xbb, 0x1c, 0x7c, 0x53, 0x1f, 0xc7, 0xb8, 0x2d, 0xba, 0x1c, 0x3c, 0x78,
|
||||
0x9c, 0x5f, 0xbf, 0x6f, 0xe6, 0xeb, 0xcc, 0xc2, 0xc1, 0x22, 0xe5, 0x6f, 0x28, 0x33, 0x56, 0x68,
|
||||
0x65, 0x55, 0x7c, 0x07, 0xd1, 0x4c, 0xab, 0xa5, 0xc8, 0x91, 0xb6, 0xa1, 0x2e, 0xb2, 0x5e, 0x70,
|
||||
0x16, 0x5c, 0x34, 0x93, 0xba, 0xc8, 0xe8, 0x09, 0x40, 0xa1, 0x55, 0x81, 0xda, 0x0a, 0x34, 0xbd,
|
||||
0xba, 0xe3, 0x1e, 0x89, 0xef, 0x81, 0x3c, 0xa5, 0x96, 0xaf, 0x9f, 0x17, 0xaf, 0xc8, 0xed, 0xbf,
|
||||
0xed, 0x23, 0x08, 0x13, 0x34, 0x9b, 0xdc, 0xd2, 0x1e, 0x44, 0x66, 0xc3, 0x39, 0x1a, 0xe3, 0xec,
|
||||
0x8d, 0x64, 0x5b, 0xd2, 0x23, 0xd8, 0x47, 0xad, 0x95, 0xae, 0xec, 0x65, 0x11, 0x0f, 0x20, 0x4c,
|
||||
0x94, 0xb1, 0xa8, 0xe9, 0x31, 0x40, 0x91, 0xa7, 0x1f, 0xa8, 0xe7, 0x22, 0x33, 0xd5, 0xec, 0x66,
|
||||
0x49, 0xa6, 0xd9, 0x77, 0xc2, 0xf6, 0x44, 0x49, 0x89, 0xdc, 0x0a, 0x25, 0xa7, 0x72, 0xa9, 0xe8,
|
||||
0x15, 0x74, 0xf9, 0x0f, 0x99, 0x1b, 0xab, 0x85, 0x5c, 0x55, 0xbe, 0xc3, 0xdf, 0x0f, 0x2f, 0x8e,
|
||||
0xc7, 0x6b, 0x20, 0x63, 0x63, 0xc4, 0x4a, 0xbe, 0xa3, 0xb4, 0x86, 0x9e, 0x42, 0xa8, 0xdd, 0x58,
|
||||
0x67, 0x20, 0xc3, 0x88, 0x95, 0x29, 0x92, 0x0a, 0xd3, 0x11, 0x74, 0xbc, 0xe6, 0x42, 0x2e, 0x95,
|
||||
0xcb, 0x4d, 0x86, 0x1d, 0xb6, 0x1b, 0x23, 0x69, 0xf3, 0x9d, 0x7a, 0xf8, 0x19, 0xc0, 0xde, 0x78,
|
||||
0x36, 0xa5, 0x03, 0x20, 0x13, 0x8d, 0xa9, 0x45, 0xb7, 0x58, 0xda, 0x60, 0xd5, 0x6d, 0xfa, 0x2d,
|
||||
0xe6, 0xad, 0x3a, 0xae, 0xd1, 0x4b, 0x20, 0x8f, 0xc2, 0x58, 0x07, 0xd1, 0xfc, 0x2d, 0xbc, 0x09,
|
||||
0xe8, 0x39, 0x90, 0x07, 0xcc, 0x71, 0xdb, 0x73, 0x47, 0xd0, 0x8f, 0x58, 0x79, 0x83, 0xb8, 0x46,
|
||||
0xaf, 0xa1, 0x5b, 0xce, 0xf6, 0xff, 0xb9, 0xc5, 0xbc, 0xca, 0x57, 0x0f, 0xa0, 0x5b, 0x76, 0xf5,
|
||||
0xd5, 0xdb, 0x8d, 0x78, 0xc2, 0x45, 0xe8, 0xde, 0xd9, 0xed, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0xf2, 0x23, 0x14, 0x36, 0x78, 0x02, 0x00, 0x00,
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
// Follow the guidelines at https://cloud.google.com/endpoints/docs/grpc/transcoding
|
||||
// to keep the gRPC service definitions friendly to REST transcoding. An excerpt:
|
||||
//
|
||||
// "Transcoding involves mapping HTTP/JSON requests and their parameters to gRPC
|
||||
// methods and their parameters and return types (we'll look at exactly how you
|
||||
// do this in the following sections). Because of this, while it's possible to
|
||||
// map an HTTP/JSON request to any arbitrary API method, it's simplest and most
|
||||
// intuitive to do so if the gRPC API itself is structured in a
|
||||
// resource-oriented way, just like a traditional HTTP REST API. In other
|
||||
// words, the API service should be designed so that it uses a small number of
|
||||
// standard methods (corresponding to HTTP verbs like GET, PUT, and so on) that
|
||||
// operate on the service's resources (and collections of resources, which are
|
||||
// themselves a type of resource).
|
||||
// These standard methods are List, Get, Create, Update, and Delete."
|
||||
//
|
||||
syntax = 'proto3';
|
||||
|
||||
service API {
|
||||
// Calls to ask the matchmaker to run a matchmaking function.
|
||||
//
|
||||
// Run MMF once. Return a matchobject that fits this profile.
|
||||
rpc CreateMatch(Profile) returns (MatchObject) {}
|
||||
// Continually run MMF and stream matchobjects that fit this profile until
|
||||
// client closes the connection.
|
||||
rpc ListMatches(Profile) returns (stream MatchObject) {}
|
||||
|
||||
// Delete a matchobject from state storage manually. (Matchobjects in state
|
||||
// storage will also automatically expire after a while)
|
||||
rpc DeleteMatch(MatchObject) returns (Result) {}
|
||||
|
||||
// Call that manage communication of DGS connection info to players.
|
||||
//
|
||||
// Write the DGS connection info for the list of players in the
|
||||
// Assignments.roster to state storage, so that info can be read by the game
|
||||
// client(s).
|
||||
// TODO: change this to be agnostic; return a 'result' instead of a connection
|
||||
// string so it can be integrated with session service etc
|
||||
rpc CreateAssignments(Assignments) returns (Result) {}
|
||||
// Remove DGS connection info for the list of players in the Roster from
|
||||
// state storage.
|
||||
rpc DeleteAssignments(Roster) returns (Result) {}
|
||||
}
|
||||
|
||||
// Data structure for a profile to pass to the matchmaking function.
|
||||
message Profile{
|
||||
string id = 1; // By convention, the CRC32 of the properties string.
|
||||
string properties = 2; // By convention, a JSON-encoded string
|
||||
}
|
||||
|
||||
// Data structure for all the properties of a match.
|
||||
message MatchObject{
|
||||
string id = 1; // By convention, a UUID
|
||||
string properties = 2; // By convention, a JSON-encoded string
|
||||
Roster roster = 3; // NYI
|
||||
}
|
||||
|
||||
// Simple message to return success/failure and error status.
|
||||
message Result{
|
||||
bool success = 1;
|
||||
string error = 2;
|
||||
}
|
||||
|
||||
// Data structure to hold a list of players in a match.
|
||||
message Roster{
|
||||
string player_ids = 1; // By convention, a space-delimited list of player IDs
|
||||
}
|
||||
|
||||
// Simple message used to pass the connection string for the DGS to the player.
|
||||
message ConnectionInfo{
|
||||
string connection_string = 1; // Passed by the matchmaker to game clients without modification.
|
||||
}
|
||||
|
||||
message Assignments{
|
||||
Roster roster = 1;
|
||||
ConnectionInfo connection_info = 2;
|
||||
}
|
@ -1,321 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: frontend.proto
|
||||
|
||||
/*
|
||||
Package frontend is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
frontend.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Group
|
||||
PlayerId
|
||||
ConnectionInfo
|
||||
Result
|
||||
*/
|
||||
package frontend
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Data structure for a group of players to pass to the matchmaking function.
|
||||
// Obviously, the group can be a group of one!
|
||||
type Group struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Properties string `protobuf:"bytes,2,opt,name=properties" json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Group) Reset() { *m = Group{} }
|
||||
func (m *Group) String() string { return proto.CompactTextString(m) }
|
||||
func (*Group) ProtoMessage() {}
|
||||
func (*Group) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *Group) GetId() string {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Group) GetProperties() string {
|
||||
if m != nil {
|
||||
return m.Properties
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type PlayerId struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (m *PlayerId) Reset() { *m = PlayerId{} }
|
||||
func (m *PlayerId) String() string { return proto.CompactTextString(m) }
|
||||
func (*PlayerId) ProtoMessage() {}
|
||||
func (*PlayerId) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *PlayerId) GetId() string {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Simple message used to pass the connection string for the DGS to the player.
|
||||
type ConnectionInfo struct {
|
||||
ConnectionString string `protobuf:"bytes,1,opt,name=connection_string,json=connectionString" json:"connection_string,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ConnectionInfo) Reset() { *m = ConnectionInfo{} }
|
||||
func (m *ConnectionInfo) String() string { return proto.CompactTextString(m) }
|
||||
func (*ConnectionInfo) ProtoMessage() {}
|
||||
func (*ConnectionInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func (m *ConnectionInfo) GetConnectionString() string {
|
||||
if m != nil {
|
||||
return m.ConnectionString
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Simple message to return success/failure and error status.
|
||||
type Result struct {
|
||||
Success bool `protobuf:"varint,1,opt,name=success" json:"success,omitempty"`
|
||||
Error string `protobuf:"bytes,2,opt,name=error" json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Result) Reset() { *m = Result{} }
|
||||
func (m *Result) String() string { return proto.CompactTextString(m) }
|
||||
func (*Result) ProtoMessage() {}
|
||||
func (*Result) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
func (m *Result) GetSuccess() bool {
|
||||
if m != nil {
|
||||
return m.Success
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Result) GetError() string {
|
||||
if m != nil {
|
||||
return m.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Group)(nil), "Group")
|
||||
proto.RegisterType((*PlayerId)(nil), "PlayerId")
|
||||
proto.RegisterType((*ConnectionInfo)(nil), "ConnectionInfo")
|
||||
proto.RegisterType((*Result)(nil), "Result")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for API service
|
||||
|
||||
type APIClient interface {
|
||||
CreateRequest(ctx context.Context, in *Group, opts ...grpc.CallOption) (*Result, error)
|
||||
DeleteRequest(ctx context.Context, in *Group, opts ...grpc.CallOption) (*Result, error)
|
||||
GetAssignment(ctx context.Context, in *PlayerId, opts ...grpc.CallOption) (*ConnectionInfo, error)
|
||||
DeleteAssignment(ctx context.Context, in *PlayerId, opts ...grpc.CallOption) (*Result, error)
|
||||
}
|
||||
|
||||
type aPIClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewAPIClient(cc *grpc.ClientConn) APIClient {
|
||||
return &aPIClient{cc}
|
||||
}
|
||||
|
||||
func (c *aPIClient) CreateRequest(ctx context.Context, in *Group, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/CreateRequest", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) DeleteRequest(ctx context.Context, in *Group, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/DeleteRequest", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) GetAssignment(ctx context.Context, in *PlayerId, opts ...grpc.CallOption) (*ConnectionInfo, error) {
|
||||
out := new(ConnectionInfo)
|
||||
err := grpc.Invoke(ctx, "/API/GetAssignment", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) DeleteAssignment(ctx context.Context, in *PlayerId, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/DeleteAssignment", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for API service
|
||||
|
||||
type APIServer interface {
|
||||
CreateRequest(context.Context, *Group) (*Result, error)
|
||||
DeleteRequest(context.Context, *Group) (*Result, error)
|
||||
GetAssignment(context.Context, *PlayerId) (*ConnectionInfo, error)
|
||||
DeleteAssignment(context.Context, *PlayerId) (*Result, error)
|
||||
}
|
||||
|
||||
func RegisterAPIServer(s *grpc.Server, srv APIServer) {
|
||||
s.RegisterService(&_API_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _API_CreateRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Group)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).CreateRequest(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/CreateRequest",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).CreateRequest(ctx, req.(*Group))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_DeleteRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Group)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).DeleteRequest(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/DeleteRequest",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).DeleteRequest(ctx, req.(*Group))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_GetAssignment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PlayerId)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).GetAssignment(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/GetAssignment",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).GetAssignment(ctx, req.(*PlayerId))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_DeleteAssignment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PlayerId)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).DeleteAssignment(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/DeleteAssignment",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).DeleteAssignment(ctx, req.(*PlayerId))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _API_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "API",
|
||||
HandlerType: (*APIServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "CreateRequest",
|
||||
Handler: _API_CreateRequest_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteRequest",
|
||||
Handler: _API_DeleteRequest_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetAssignment",
|
||||
Handler: _API_GetAssignment_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteAssignment",
|
||||
Handler: _API_DeleteAssignment_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "frontend.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("frontend.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 260 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0x41, 0x4b, 0xfb, 0x40,
|
||||
0x10, 0xc5, 0x9b, 0xfc, 0x69, 0xda, 0x0e, 0x34, 0xff, 0xba, 0x78, 0x08, 0x39, 0x88, 0xec, 0xa9,
|
||||
0x20, 0xee, 0x41, 0x0f, 0x7a, 0xf1, 0x50, 0x2a, 0x94, 0xdc, 0x4a, 0xfc, 0x00, 0x52, 0x93, 0x69,
|
||||
0x59, 0x88, 0xbb, 0x71, 0x66, 0x72, 0xf0, 0x0b, 0xf9, 0x39, 0xc5, 0x4d, 0x6b, 0x55, 0xc4, 0xe3,
|
||||
0xfb, 0xed, 0x7b, 0x8f, 0x7d, 0x03, 0xe9, 0x96, 0xbc, 0x13, 0x74, 0xb5, 0x69, 0xc9, 0x8b, 0xd7,
|
||||
0x37, 0x30, 0x5c, 0x91, 0xef, 0x5a, 0x95, 0x42, 0x6c, 0xeb, 0x2c, 0x3a, 0x8f, 0xe6, 0x93, 0x32,
|
||||
0xb6, 0xb5, 0x3a, 0x03, 0x68, 0xc9, 0xb7, 0x48, 0x62, 0x91, 0xb3, 0x38, 0xf0, 0x2f, 0x44, 0xe7,
|
||||
0x30, 0x5e, 0x37, 0x9b, 0x57, 0xa4, 0xa2, 0xfe, 0x99, 0xd5, 0x77, 0x90, 0x2e, 0xbd, 0x73, 0x58,
|
||||
0x89, 0xf5, 0xae, 0x70, 0x5b, 0xaf, 0x2e, 0xe0, 0xa4, 0xfa, 0x24, 0x8f, 0x2c, 0x64, 0xdd, 0x6e,
|
||||
0x1f, 0x98, 0x1d, 0x1f, 0x1e, 0x02, 0xd7, 0xb7, 0x90, 0x94, 0xc8, 0x5d, 0x23, 0x2a, 0x83, 0x11,
|
||||
0x77, 0x55, 0x85, 0xcc, 0xc1, 0x3c, 0x2e, 0x0f, 0x52, 0x9d, 0xc2, 0x10, 0x89, 0x3c, 0xed, 0x7f,
|
||||
0xd6, 0x8b, 0xab, 0xb7, 0x08, 0xfe, 0x2d, 0xd6, 0x85, 0xd2, 0x30, 0x5d, 0x12, 0x6e, 0x04, 0x4b,
|
||||
0x7c, 0xe9, 0x90, 0x45, 0x25, 0x26, 0xac, 0xcc, 0x47, 0xa6, 0x6f, 0xd6, 0x83, 0x0f, 0xcf, 0x3d,
|
||||
0x36, 0xf8, 0xa7, 0xe7, 0x12, 0xa6, 0x2b, 0x94, 0x05, 0xb3, 0xdd, 0xb9, 0x67, 0x74, 0xa2, 0x26,
|
||||
0xe6, 0x30, 0x3a, 0xff, 0x6f, 0xbe, 0x6f, 0xd4, 0x03, 0x35, 0x87, 0x59, 0x5f, 0xf9, 0x7b, 0xe2,
|
||||
0x58, 0xfc, 0x94, 0x84, 0xeb, 0x5f, 0xbf, 0x07, 0x00, 0x00, 0xff, 0xff, 0x2b, 0xde, 0x2c, 0x5b,
|
||||
0x8f, 0x01, 0x00, 0x00,
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// -------------
|
||||
// Follow the guidelines at https://cloud.google.com/endpoints/docs/grpc/transcoding
|
||||
// to keep the gRPC service definitions friendly to REST transcoding. An excerpt:
|
||||
//
|
||||
// "Transcoding involves mapping HTTP/JSON requests and their parameters to gRPC
|
||||
// methods and their parameters and return types (we'll look at exactly how you
|
||||
// do this in the following sections). Because of this, while it's possible to
|
||||
// map an HTTP/JSON request to any arbitrary API method, it's simplest and most
|
||||
// intuitive to do so if the gRPC API itself is structured in a
|
||||
// resource-oriented way, just like a traditional HTTP REST API. In other
|
||||
// words, the API service should be designed so that it uses a small number of
|
||||
// standard methods (corresponding to HTTP verbs like GET, PUT, and so on) that
|
||||
// operate on the service's resources (and collections of resources, which are
|
||||
// themselves a type of resource).
|
||||
// These standard methods are List, Get, Create, Update, and Delete."
|
||||
//
|
||||
syntax = 'proto3';
|
||||
|
||||
service API {
|
||||
rpc CreateRequest(Group) returns (Result) {}
|
||||
rpc DeleteRequest(Group) returns (Result) {}
|
||||
rpc GetAssignment(PlayerId) returns (ConnectionInfo) {}
|
||||
rpc DeleteAssignment(PlayerId) returns (Result) {}
|
||||
}
|
||||
|
||||
// Data structure for a group of players to pass to the matchmaking function.
|
||||
// Obviously, the group can be a group of one!
|
||||
message Group{
|
||||
string id = 1; // By convention, string of space-delimited playerIDs
|
||||
string properties = 2; // By convention, a JSON-encoded string
|
||||
}
|
||||
|
||||
message PlayerId {
|
||||
string id = 1; // By convention, a UUID
|
||||
}
|
||||
|
||||
// Simple message used to pass the connection string for the DGS to the player.
|
||||
message ConnectionInfo{
|
||||
string connection_string = 1; // Passed by the matchmaker to game clients without modification.
|
||||
}
|
||||
|
||||
// Simple message to return success/failure and error status.
|
||||
message Result{
|
||||
bool success = 1;
|
||||
string error = 2;
|
||||
}
|
101
api/query.proto
Normal file
101
api/query.proto
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
package openmatch;
|
||||
option go_package = "open-match.dev/open-match/pkg/pb";
|
||||
option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "MM Logic (Data Layer)"
|
||||
version: "1.0"
|
||||
contact: {
|
||||
name: "Open Match"
|
||||
url: "https://open-match.dev"
|
||||
email: "open-match-discuss@googlegroups.com"
|
||||
}
|
||||
license: {
|
||||
name: "Apache 2.0 License"
|
||||
url: "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
}
|
||||
external_docs: {
|
||||
url: "https://open-match.dev/site/docs/"
|
||||
description: "Open Match Documentation"
|
||||
}
|
||||
schemes: HTTP
|
||||
schemes: HTTPS
|
||||
consumes: "application/json"
|
||||
produces: "application/json"
|
||||
responses: {
|
||||
key: "404"
|
||||
value: {
|
||||
description: "Returned when the resource does not exist."
|
||||
schema: { json_schema: { type: STRING } }
|
||||
}
|
||||
}
|
||||
// TODO Add annotations for security_defintiions.
|
||||
// See
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/proto/examplepb/a_bit_of_everything.proto
|
||||
};
|
||||
|
||||
message QueryTicketsRequest {
|
||||
// The Pool representing the set of Filters to be queried.
|
||||
Pool pool = 1;
|
||||
}
|
||||
|
||||
message QueryTicketsResponse {
|
||||
// Tickets that meet all the filtering criteria requested by the pool.
|
||||
repeated Ticket tickets = 1;
|
||||
}
|
||||
|
||||
message QueryTicketIdsRequest {
|
||||
// The Pool representing the set of Filters to be queried.
|
||||
Pool pool = 1;
|
||||
}
|
||||
|
||||
message QueryTicketIdsResponse {
|
||||
// TicketIDs that meet all the filtering criteria requested by the pool.
|
||||
repeated string ids = 1;
|
||||
}
|
||||
|
||||
// The QueryService service implements helper APIs for Match Function to query Tickets from state storage.
|
||||
service QueryService {
|
||||
// QueryTickets gets a list of Tickets that match all Filters of the input Pool.
|
||||
// - If the Pool contains no Filters, QueryTickets will return all Tickets in the state storage.
|
||||
// QueryTickets pages the Tickets by `queryPageSize` and stream back responses.
|
||||
// - queryPageSize is default to 1000 if not set, and has a minimum of 10 and maximum of 10000.
|
||||
rpc QueryTickets(QueryTicketsRequest) returns (stream QueryTicketsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/queryservice/tickets:query"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// QueryTicketIds gets the list of TicketIDs that meet all the filtering criteria requested by the pool.
|
||||
// - If the Pool contains no Filters, QueryTicketIds will return all TicketIDs in the state storage.
|
||||
// QueryTicketIds pages the TicketIDs by `queryPageSize` and stream back responses.
|
||||
// - queryPageSize is default to 1000 if not set, and has a minimum of 10 and maximum of 10000.
|
||||
rpc QueryTicketIds(QueryTicketIdsRequest) returns (stream QueryTicketIdsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/queryservice/ticketids:query"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
366
api/query.swagger.json
Normal file
366
api/query.swagger.json
Normal file
@ -0,0 +1,366 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "MM Logic (Data Layer)",
|
||||
"version": "1.0",
|
||||
"contact": {
|
||||
"name": "Open Match",
|
||||
"url": "https://open-match.dev",
|
||||
"email": "open-match-discuss@googlegroups.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0 License",
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/queryservice/ticketids:query": {
|
||||
"post": {
|
||||
"summary": "QueryTicketIds gets the list of TicketIDs that meet all the filtering criteria requested by the pool.\n - If the Pool contains no Filters, QueryTicketIds will return all TicketIDs in the state storage.\nQueryTicketIds pages the TicketIDs by `queryPageSize` and stream back responses.\n - queryPageSize is default to 1000 if not set, and has a minimum of 10 and maximum of 10000.",
|
||||
"operationId": "QueryTicketIds",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchQueryTicketIdsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchQueryTicketIdsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"QueryService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/queryservice/tickets:query": {
|
||||
"post": {
|
||||
"summary": "QueryTickets gets a list of Tickets that match all Filters of the input Pool.\n - If the Pool contains no Filters, QueryTickets will return all Tickets in the state storage.\nQueryTickets pages the Tickets by `queryPageSize` and stream back responses.\n - queryPageSize is default to 1000 if not set, and has a minimum of 10 and maximum of 10000.",
|
||||
"operationId": "QueryTickets",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchQueryTicketsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchQueryTicketsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"QueryService"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"openmatchAssignment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"connection": {
|
||||
"type": "string",
|
||||
"description": "Connection information for this Assignment."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchDoubleRangeFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"double_arg": {
|
||||
"type": "string",
|
||||
"description": "Name of the ticket's search_fields.double_args this Filter operates on."
|
||||
},
|
||||
"max": {
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Maximum value."
|
||||
},
|
||||
"min": {
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Minimum value."
|
||||
}
|
||||
},
|
||||
"title": "Filters numerical values to only those within a range.\n double_arg: \"foo\"\n max: 10\n min: 5\nmatches:\n {\"foo\": 5}\n {\"foo\": 7.5}\n {\"foo\": 10}\ndoes not match:\n {\"foo\": 4}\n {\"foo\": 10.01}\n {\"foo\": \"7.5\"}\n {}"
|
||||
},
|
||||
"openmatchPool": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "A developer-chosen human-readable name for this Pool."
|
||||
},
|
||||
"double_range_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchDoubleRangeFilter"
|
||||
},
|
||||
"description": "Set of Filters indicating the filtering criteria. Selected tickets must\nmatch every Filter."
|
||||
},
|
||||
"string_equals_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchStringEqualsFilter"
|
||||
}
|
||||
},
|
||||
"tag_present_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchTagPresentFilter"
|
||||
}
|
||||
},
|
||||
"created_before": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "If specified, only Tickets created before the specified time are selected."
|
||||
},
|
||||
"created_after": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "If specified, only Tickets created after the specified time are selected."
|
||||
}
|
||||
},
|
||||
"description": "Pool specfies a set of criteria that are used to select a subset of Tickets\nthat meet all the criteria."
|
||||
},
|
||||
"openmatchQueryTicketIdsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pool": {
|
||||
"$ref": "#/definitions/openmatchPool",
|
||||
"description": "The Pool representing the set of Filters to be queried."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchQueryTicketIdsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "TicketIDs that meet all the filtering criteria requested by the pool."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchQueryTicketsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pool": {
|
||||
"$ref": "#/definitions/openmatchPool",
|
||||
"description": "The Pool representing the set of Filters to be queried."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchQueryTicketsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tickets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchTicket"
|
||||
},
|
||||
"description": "Tickets that meet all the filtering criteria requested by the pool."
|
||||
}
|
||||
}
|
||||
},
|
||||
"openmatchSearchFields": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"double_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"description": "Float arguments. Filterable on ranges."
|
||||
},
|
||||
"string_args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "String arguments. Filterable on equality."
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Filterable on presence or absence of given value."
|
||||
}
|
||||
},
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"openmatchStringEqualsFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"string_arg": {
|
||||
"type": "string",
|
||||
"description": "Name of the ticket's search_fields.string_args this Filter operates on."
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Filters strings exactly equaling a value.\n string_arg: \"foo\"\n value: \"bar\"\nmatches:\n {\"foo\": \"bar\"}\ndoes not match:\n {\"foo\": \"baz\"}\n {\"bar\": \"foo\"}\n {}"
|
||||
},
|
||||
"openmatchTagPresentFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Filters to the tag being present on the search_fields.\n tag: \"foo\"\nmatches:\n [\"foo\"]\n [\"bar\",\"foo\"]\ndoes not match:\n [\"bar\"]\n []"
|
||||
},
|
||||
"openmatchTicket": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type_url": {
|
||||
"type": "string",
|
||||
"description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics."
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "Must be a valid serialized protocol buffer of the above specified type."
|
||||
}
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchQueryTicketIdsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchQueryTicketIdsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchQueryTicketIdsResponse"
|
||||
},
|
||||
"openmatchQueryTicketsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchQueryTicketsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchQueryTicketsResponse"
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Open Match Documentation",
|
||||
"url": "https://open-match.dev/site/docs/"
|
||||
}
|
||||
}
|
176
cloudbuild.yaml
Normal file
176
cloudbuild.yaml
Normal file
@ -0,0 +1,176 @@
|
||||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
################################################################################
|
||||
# Open Match Script for Google Cloud Build #
|
||||
################################################################################
|
||||
|
||||
# To run this locally:
|
||||
# cloud-build-local --config=cloudbuild.yaml --dryrun=false --substitutions=_OM_VERSION=DEV .
|
||||
# To run this remotely:
|
||||
# gcloud builds submit --config=cloudbuild.yaml --substitutions=_OM_VERSION=DEV .
|
||||
|
||||
# Requires gcloud to be installed to work. (https://cloud.google.com/sdk/)
|
||||
# gcloud auth login
|
||||
# gcloud components install cloud-build-local
|
||||
|
||||
# This YAML contains all the build steps for building Open Match.
|
||||
# All PRs are verified against this script to prevent build breakages and regressions.
|
||||
|
||||
# Conventions
|
||||
# Each build step is ID'ed with "Prefix: Description".
|
||||
# The prefix portion determines what kind of step it is and it's impact.
|
||||
# Docker Image: Read-Only, outputs a docker image.
|
||||
# Lint: Read-Only, verifies correctness and formatting of a file.
|
||||
# Build: Read-Write, outputs a build artifact. Ok to run in parallel if the artifact will not collide with another one.
|
||||
# Generate: Read-Write, outputs files within /workspace that are used in other build step. Do not run these in parallel.
|
||||
# Setup: Read-Write, similar to generate but steps that run before any other step.
|
||||
|
||||
# Some useful things to know about Cloud Build.
|
||||
# The root of this repository is always stored in /workspace.
|
||||
# Any modifications that occur within /workspace are persisted between builds anything else is forgotten.
|
||||
# If a build step has intermediate files that need to be persisted for a future step then use volumes.
|
||||
# An example of this is the go-vol which is where the pkg/ data for go mod is stored.
|
||||
# More information here: https://cloud.google.com/cloud-build/docs/build-config#build_steps
|
||||
# A build step is basically a docker image that is tuned for Cloud Build,
|
||||
# https://github.com/GoogleCloudPlatform/cloud-builders/tree/master/go
|
||||
|
||||
steps:
|
||||
- id: 'Docker Image: open-match-build'
|
||||
name: gcr.io/cloud-builders/docker
|
||||
args: ['build', '-t', 'gcr.io/$PROJECT_ID/open-match-build', '-f', 'Dockerfile.ci', '.']
|
||||
waitFor: ['-']
|
||||
|
||||
- id: 'Build: Clean'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'clean-third-party', 'clean-protos', 'clean-swagger-docs']
|
||||
waitFor: ['Docker Image: open-match-build']
|
||||
|
||||
# - id: 'Test: Markdown'
|
||||
# name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
# args: ['make', 'md-test']
|
||||
# waitFor: ['Build: Clean']
|
||||
|
||||
- id: 'Setup: Download Dependencies'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'sync-deps']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
path: '/go'
|
||||
waitFor: ['Build: Clean']
|
||||
|
||||
- id: 'Build: Initialize Toolchain'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'install-toolchain']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
path: '/go'
|
||||
waitFor: ['Setup: Download Dependencies']
|
||||
|
||||
- id: 'Test: Terraform Configuration'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'terraform-test']
|
||||
waitFor: ['Build: Initialize Toolchain']
|
||||
|
||||
- id: 'Build: Deployment Configs'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'SHORT_SHA=${SHORT_SHA}', 'update-chart-deps', 'install/yaml/']
|
||||
waitFor: ['Build: Initialize Toolchain']
|
||||
|
||||
- id: 'Build: Assets'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'assets', '-j12']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
path: '/go'
|
||||
waitFor: ['Build: Deployment Configs']
|
||||
|
||||
- id: 'Build: Binaries'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'GOPROXY=off', 'build', 'all', '-j12']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
path: '/go'
|
||||
waitFor: ['Build: Assets']
|
||||
|
||||
- id: 'Test: Services'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'GOPROXY=off', 'GOLANG_TEST_COUNT=10', 'test']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
path: '/go'
|
||||
waitFor: ['Build: Assets']
|
||||
|
||||
- id: 'Build: Docker Images'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', '_GCB_POST_SUBMIT=${_GCB_POST_SUBMIT}', '_GCB_LATEST_VERSION=${_GCB_LATEST_VERSION}', 'SHORT_SHA=${SHORT_SHA}', 'BRANCH_NAME=${BRANCH_NAME}', 'push-images', '-j8']
|
||||
waitFor: ['Build: Assets']
|
||||
|
||||
- id: 'Lint: Format, Vet, Charts'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'lint']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
path: '/go'
|
||||
waitFor: ['Build: Assets']
|
||||
|
||||
- id: 'Test: Deploy Open Match'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'SHORT_SHA=${SHORT_SHA}', 'OPEN_MATCH_KUBERNETES_NAMESPACE=open-match-${BUILD_ID}', 'OPEN_MATCH_RELEASE_NAME=open-match-${BUILD_ID}', 'auth-gke-cluster', 'delete-chart', 'ci-reap-namespaces', 'install-ci-chart']
|
||||
waitFor: ['Build: Docker Images']
|
||||
|
||||
- id: 'Deploy: Deployment Configs'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', '_GCB_POST_SUBMIT=${_GCB_POST_SUBMIT}', '_GCB_LATEST_VERSION=${_GCB_LATEST_VERSION}', 'SHORT_SHA=${SHORT_SHA}', 'BRANCH_NAME=${BRANCH_NAME}', 'ci-deploy-artifacts']
|
||||
waitFor: ['Lint: Format, Vet, Charts', 'Test: Deploy Open Match']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
path: '/go'
|
||||
|
||||
- id: 'Test: End-to-End Cluster'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'GOPROXY=off', 'SHORT_SHA=${SHORT_SHA}', 'OPEN_MATCH_KUBERNETES_NAMESPACE=open-match-${BUILD_ID}', 'test-e2e-cluster']
|
||||
waitFor: ['Test: Deploy Open Match', 'Build: Assets']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
path: '/go'
|
||||
|
||||
- id: 'Test: Delete Open Match'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'GCLOUD_EXTRA_FLAGS=--async', 'SHORT_SHA=${SHORT_SHA}', 'OPEN_MATCH_KUBERNETES_NAMESPACE=open-match-${BUILD_ID}', 'OPEN_MATCH_RELEASE_NAME=open-match-${BUILD_ID}', 'GCP_PROJECT_ID=${PROJECT_ID}', 'delete-chart']
|
||||
waitFor: ['Test: End-to-End Cluster']
|
||||
|
||||
artifacts:
|
||||
objects:
|
||||
location: '${_ARTIFACTS_BUCKET}'
|
||||
paths:
|
||||
- install/yaml/install.yaml
|
||||
- install/yaml/01-open-match-core.yaml
|
||||
- install/yaml/02-open-match-demo.yaml
|
||||
- install/yaml/03-prometheus-chart.yaml
|
||||
- install/yaml/04-grafana-chart.yaml
|
||||
- install/yaml/05-jaeger-chart.yaml
|
||||
- install/yaml/06-open-match-override-configmap.yaml
|
||||
|
||||
substitutions:
|
||||
_OM_VERSION: "1.1.0"
|
||||
_GCB_POST_SUBMIT: "0"
|
||||
_GCB_LATEST_VERSION: "undefined"
|
||||
_ARTIFACTS_BUCKET: "gs://open-match-build-artifacts/output/"
|
||||
_LOGS_BUCKET: "gs://open-match-build-logs/"
|
||||
logsBucket: '${_LOGS_BUCKET}'
|
||||
options:
|
||||
sourceProvenanceHash: ['SHA256']
|
||||
machineType: 'N1_HIGHCPU_32'
|
||||
timeout: 2500s
|
@ -1,9 +0,0 @@
|
||||
steps:
|
||||
- name: 'gcr.io/cloud-builders/docker'
|
||||
args: [
|
||||
'build',
|
||||
'--tag=gcr.io/$PROJECT_ID/openmatch-backendapi:dev',
|
||||
'-f', 'Dockerfile.backendapi',
|
||||
'.'
|
||||
]
|
||||
images: ['gcr.io/$PROJECT_ID/openmatch-backendapi:dev']
|
@ -1,10 +0,0 @@
|
||||
steps:
|
||||
- name: 'gcr.io/cloud-builders/docker'
|
||||
args: [
|
||||
'build',
|
||||
'--tag=gcr.io/$PROJECT_ID/openmatch-evaluator:dev',
|
||||
'-f', 'Dockerfile.evaluator',
|
||||
'.'
|
||||
]
|
||||
images: ['gcr.io/$PROJECT_ID/openmatch-evaluator:dev']
|
||||
|
@ -1,9 +0,0 @@
|
||||
steps:
|
||||
- name: 'gcr.io/cloud-builders/docker'
|
||||
args: [
|
||||
'build',
|
||||
'--tag=gcr.io/$PROJECT_ID/openmatch-frontendapi:dev',
|
||||
'-f', 'Dockerfile.frontendapi',
|
||||
'.'
|
||||
]
|
||||
images: ['gcr.io/$PROJECT_ID/openmatch-frontendapi:dev']
|
@ -1,9 +0,0 @@
|
||||
steps:
|
||||
- name: 'gcr.io/cloud-builders/docker'
|
||||
args: [
|
||||
'build',
|
||||
'--tag=gcr.io/$PROJECT_ID/openmatch-mmf:dev',
|
||||
'-f', 'Dockerfile.mmf',
|
||||
'.'
|
||||
]
|
||||
images: ['gcr.io/$PROJECT_ID/openmatch-mmf:dev']
|
@ -1,9 +0,0 @@
|
||||
steps:
|
||||
- name: 'gcr.io/cloud-builders/docker'
|
||||
args: [
|
||||
'build',
|
||||
'--tag=gcr.io/$PROJECT_ID/openmatch-mmforc:dev',
|
||||
'-f', 'Dockerfile.mmforc',
|
||||
'.'
|
||||
]
|
||||
images: ['gcr.io/$PROJECT_ID/openmatch-mmforc:dev']
|
25
cmd/backend/backend.go
Normal file
25
cmd/backend/backend.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package main is the backend service for Open Match.
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app/backend"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appmain.RunApplication("backend", backend.BindService)
|
||||
}
|
@ -1,401 +0,0 @@
|
||||
/*
|
||||
package apisrv provides an implementation of the gRPC server defined in ../proto/backend.proto
|
||||
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
package apisrv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
backend "github.com/GoogleCloudPlatform/open-match/cmd/backendapi/proto"
|
||||
"github.com/GoogleCloudPlatform/open-match/internal/metrics"
|
||||
redisHelpers "github.com/GoogleCloudPlatform/open-match/internal/statestorage/redis"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/plugin/ocgrpc"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Logrus structured logging setup
|
||||
var (
|
||||
beLogFields = log.Fields{
|
||||
"app": "openmatch",
|
||||
"component": "backend",
|
||||
"caller": "backend/apisrv/apisrv.go",
|
||||
}
|
||||
beLog = log.WithFields(beLogFields)
|
||||
)
|
||||
|
||||
// BackendAPI implements backend.ApiServer, the server generated by compiling
|
||||
// the protobuf, by fulfilling the backend.APIClient interface.
|
||||
type BackendAPI struct {
|
||||
grpc *grpc.Server
|
||||
cfg *viper.Viper
|
||||
pool *redis.Pool
|
||||
}
|
||||
type backendAPI BackendAPI
|
||||
|
||||
// New returns an instantiated srvice
|
||||
func New(cfg *viper.Viper, pool *redis.Pool) *BackendAPI {
|
||||
s := BackendAPI{
|
||||
pool: pool,
|
||||
grpc: grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{})),
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
// Add a hook to the logger to auto-count log lines for metrics output thru OpenCensus
|
||||
log.AddHook(metrics.NewHook(BeLogLines, KeySeverity))
|
||||
|
||||
backend.RegisterAPIServer(s.grpc, (*backendAPI)(&s))
|
||||
beLog.Info("Successfully registered gRPC server")
|
||||
return &s
|
||||
}
|
||||
|
||||
// Open opens the api grpc service, starting it listening on the configured port.
|
||||
func (s *BackendAPI) Open() error {
|
||||
ln, err := net.Listen("tcp", ":"+s.cfg.GetString("api.backend.port"))
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"port": s.cfg.GetInt("api.backend.port"),
|
||||
}).Error("net.Listen() error")
|
||||
return err
|
||||
}
|
||||
|
||||
beLog.WithFields(log.Fields{"port": s.cfg.GetInt("api.backend.port")}).Info("TCP net listener initialized")
|
||||
|
||||
go func() {
|
||||
err := s.grpc.Serve(ln)
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{"error": err.Error()}).Error("gRPC serve() error")
|
||||
}
|
||||
beLog.Info("serving gRPC endpoints")
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateMatch is this service's implementation of the CreateMatch gRPC method
|
||||
// defined in ../proto/backend.proto
|
||||
func (s *backendAPI) CreateMatch(c context.Context, p *backend.Profile) (*backend.MatchObject, error) {
|
||||
|
||||
// Get a cancel-able context
|
||||
ctx, cancel := context.WithCancel(c)
|
||||
defer cancel()
|
||||
|
||||
// Create context for tagging OpenCensus metrics.
|
||||
funcName := "CreateMatch"
|
||||
fnCtx, _ := tag.New(ctx, tag.Insert(KeyMethod, funcName))
|
||||
|
||||
beLog = beLog.WithFields(log.Fields{"func": funcName})
|
||||
beLog.WithFields(log.Fields{
|
||||
"profileID": p.Id,
|
||||
}).Info("gRPC call executing")
|
||||
|
||||
// Write profile
|
||||
_, err := redisHelpers.Create(ctx, s.pool, p.Id, p.Properties)
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"component": "statestorage",
|
||||
}).Error("Statestorage failure to create match profile")
|
||||
|
||||
// Failure! Return empty match object and the error
|
||||
stats.Record(fnCtx, BeGrpcErrors.M(1))
|
||||
return &backend.MatchObject{}, err
|
||||
}
|
||||
|
||||
beLog.WithFields(log.Fields{
|
||||
"profileID": p.Id,
|
||||
}).Info("Profile written to statestorage")
|
||||
|
||||
// Generate a request to fill the profile
|
||||
moID := strings.Replace(uuid.New().String(), "-", "", -1)
|
||||
profileRequestKey := moID + "." + p.Id
|
||||
|
||||
_, err = redisHelpers.Update(ctx, s.pool, s.cfg.GetString("queues.profiles.name"), profileRequestKey)
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"component": "statestorage",
|
||||
}).Error("Statestorage failure to queue profile")
|
||||
|
||||
// Failure! Return empty match object and the error
|
||||
stats.Record(fnCtx, BeGrpcErrors.M(1))
|
||||
return &backend.MatchObject{}, err
|
||||
}
|
||||
|
||||
beLog.WithFields(log.Fields{
|
||||
"profileID": p.Id,
|
||||
"matchObjectID": moID,
|
||||
"profileRequestKey": profileRequestKey,
|
||||
}).Info("Profile added to processing queue")
|
||||
|
||||
// get and return matchobject
|
||||
watchChan := redisHelpers.Watcher(ctx, s.pool, profileRequestKey) // Watcher() runs the appropriate Redis commands.
|
||||
mo := &backend.MatchObject{Id: p.Id, Properties: ""}
|
||||
errString := ("Error retrieving matchmaking results from statestorage")
|
||||
timeout := time.Duration(s.cfg.GetInt("interval.resultsTimeout")) * time.Second
|
||||
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
// TODO:Timeout: deal with the fallout. There are some edge cases here.
|
||||
// When there is a timeout, need to send a stop to the watch channel.
|
||||
stats.Record(fnCtx, BeGrpcRequests.M(1))
|
||||
return mo, errors.New(errString + ": timeout exceeded")
|
||||
|
||||
case properties, ok := <-watchChan:
|
||||
if !ok {
|
||||
// ok is false if watchChan has been closed by redisHelpers.Watcher()
|
||||
stats.Record(fnCtx, BeGrpcRequests.M(1))
|
||||
return mo, errors.New(errString + ": channel closed - was the context cancelled?")
|
||||
}
|
||||
|
||||
beLog.WithFields(log.Fields{
|
||||
"profileRequestKey": profileRequestKey,
|
||||
"matchObjectID": moID,
|
||||
// DEBUG ONLY: This prints the entire result from redis to the logs
|
||||
"matchProperties": properties, // very verbose!
|
||||
}).Debug("Received match object from statestorage")
|
||||
|
||||
// 'ok' was true, so properties should contain the results from redis.
|
||||
// Do some error checking on the returned JSON
|
||||
if !gjson.Valid(properties) {
|
||||
// Just splitting this across lines for readability/wrappability
|
||||
thisError := ": Retreived json was malformed"
|
||||
thisError = thisError + " - did the evaluator write a valid JSON match object?"
|
||||
stats.Record(fnCtx, BeGrpcErrors.M(1))
|
||||
return mo, errors.New(errString + thisError)
|
||||
}
|
||||
|
||||
mmfError := gjson.Get(properties, "error")
|
||||
if mmfError.Exists() {
|
||||
stats.Record(fnCtx, BeGrpcErrors.M(1))
|
||||
return mo, errors.New(errString + ": " + mmfError.String())
|
||||
}
|
||||
|
||||
// Passed error checking; safe to send this property blob to the calling client.
|
||||
mo.Properties = properties
|
||||
}
|
||||
|
||||
beLog.WithFields(log.Fields{
|
||||
"profileID": p.Id,
|
||||
"matchObjectID": moID,
|
||||
"profileRequestKey": profileRequestKey,
|
||||
}).Info("Matchmaking results received, returning to backend client")
|
||||
|
||||
stats.Record(fnCtx, BeGrpcRequests.M(1))
|
||||
return mo, err
|
||||
}
|
||||
|
||||
// ListMatches is this service's implementation of the ListMatches gRPC method
|
||||
// defined in ../proto/backend.proto
|
||||
// This is the steaming version of CreateMatch - continually submitting the profile to be filled
|
||||
// until the requesting service ends the connection.
|
||||
func (s *backendAPI) ListMatches(p *backend.Profile, matchStream backend.API_ListMatchesServer) error {
|
||||
|
||||
// call creatematch in infinite loop as long as the stream is open
|
||||
ctx := matchStream.Context() // https://talks.golang.org/2015/gotham-grpc.slide#30
|
||||
|
||||
// Create context for tagging OpenCensus metrics.
|
||||
funcName := "ListMatches"
|
||||
fnCtx, _ := tag.New(ctx, tag.Insert(KeyMethod, funcName))
|
||||
|
||||
beLog = beLog.WithFields(log.Fields{"func": funcName})
|
||||
beLog.WithFields(log.Fields{
|
||||
"profileID": p.Id,
|
||||
}).Info("gRPC call executing. Calling CreateMatch. Looping until cancelled.")
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Context cancelled, probably because the client cancelled their request, time to exit.
|
||||
beLog.WithFields(log.Fields{
|
||||
"profileID": p.Id,
|
||||
}).Info("gRPC Context cancelled; client is probably finished receiving matches")
|
||||
|
||||
// TODO: need to make sure that in-flight matches don't get leaked here.
|
||||
stats.Record(fnCtx, BeGrpcRequests.M(1))
|
||||
return nil
|
||||
|
||||
default:
|
||||
// Retreive results from Redis
|
||||
mo, err := s.CreateMatch(ctx, p)
|
||||
|
||||
beLog = beLog.WithFields(log.Fields{"func": funcName})
|
||||
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{"error": err.Error()}).Error("Failure calling CreateMatch")
|
||||
stats.Record(fnCtx, BeGrpcErrors.M(1))
|
||||
return err
|
||||
}
|
||||
beLog.WithFields(log.Fields{"matchProperties": fmt.Sprintf("%v", &mo)}).Debug("Streaming back match object")
|
||||
matchStream.Send(mo)
|
||||
|
||||
// TODO: This should be tunable, but there should be SOME sleep here, to give a requestor a window
|
||||
// to cleanly close the connection after receiving a match object when they know they don't want to
|
||||
// request any more matches.
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteMatch is this service's implementation of the DeleteMatch gRPC method
|
||||
// defined in ../proto/backend.proto
|
||||
func (s *backendAPI) DeleteMatch(ctx context.Context, mo *backend.MatchObject) (*backend.Result, error) {
|
||||
|
||||
// Create context for tagging OpenCensus metrics.
|
||||
funcName := "DeleteMatch"
|
||||
fnCtx, _ := tag.New(ctx, tag.Insert(KeyMethod, funcName))
|
||||
|
||||
beLog = beLog.WithFields(log.Fields{"func": funcName})
|
||||
beLog.WithFields(log.Fields{
|
||||
"matchObjectID": mo.Id,
|
||||
}).Info("gRPC call executing")
|
||||
|
||||
_, err := redisHelpers.Delete(ctx, s.pool, mo.Id)
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"component": "statestorage",
|
||||
}).Error("Statestorage error")
|
||||
|
||||
stats.Record(fnCtx, BeGrpcErrors.M(1))
|
||||
return &backend.Result{Success: false, Error: err.Error()}, err
|
||||
}
|
||||
|
||||
beLog.WithFields(log.Fields{
|
||||
"matchObjectID": mo.Id,
|
||||
}).Info("Match Object deleted.")
|
||||
|
||||
stats.Record(fnCtx, BeGrpcRequests.M(1))
|
||||
return &backend.Result{Success: true, Error: ""}, err
|
||||
}
|
||||
|
||||
// CreateAssignments is this service's implementation of the CreateAssignments gRPC method
|
||||
// defined in ../proto/backend.proto
|
||||
func (s *backendAPI) CreateAssignments(ctx context.Context, a *backend.Assignments) (*backend.Result, error) {
|
||||
|
||||
// TODO: make playerIDs a repeated protobuf message field and iterate over it
|
||||
assignments := strings.Split(a.Roster.PlayerIds, " ")
|
||||
|
||||
// Create context for tagging OpenCensus metrics.
|
||||
funcName := "CreateAssignments"
|
||||
fnCtx, _ := tag.New(ctx, tag.Insert(KeyMethod, funcName))
|
||||
|
||||
beLog = beLog.WithFields(log.Fields{"func": funcName})
|
||||
beLog.WithFields(log.Fields{
|
||||
"numAssignments": len(assignments),
|
||||
}).Info("gRPC call executing")
|
||||
|
||||
// TODO: relocate this redis functionality to a module
|
||||
redisConn := s.pool.Get()
|
||||
defer redisConn.Close()
|
||||
|
||||
// Create player assignments in a transaction
|
||||
redisConn.Send("MULTI")
|
||||
for _, playerID := range assignments {
|
||||
beLog.WithFields(log.Fields{
|
||||
"query": "HSET",
|
||||
"playerID": playerID,
|
||||
s.cfg.GetString("jsonkeys.connstring"): a.ConnectionInfo.ConnectionString,
|
||||
}).Debug("Statestorage operation")
|
||||
redisConn.Send("HSET", playerID, s.cfg.GetString("jsonkeys.connstring"), a.ConnectionInfo.ConnectionString)
|
||||
}
|
||||
_, err := redisConn.Do("EXEC")
|
||||
|
||||
// Issue encountered
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"component": "statestorage",
|
||||
}).Error("Statestorage error")
|
||||
|
||||
stats.Record(fnCtx, BeGrpcErrors.M(1))
|
||||
stats.Record(fnCtx, BeAssignmentFailures.M(int64(len(assignments))))
|
||||
return &backend.Result{Success: false, Error: err.Error()}, err
|
||||
}
|
||||
|
||||
// Success!
|
||||
beLog.WithFields(log.Fields{
|
||||
"numAssignments": len(assignments),
|
||||
}).Info("Assignments complete")
|
||||
|
||||
stats.Record(fnCtx, BeGrpcRequests.M(1))
|
||||
stats.Record(fnCtx, BeAssignments.M(int64(len(assignments))))
|
||||
return &backend.Result{Success: true, Error: ""}, err
|
||||
}
|
||||
|
||||
// DeleteAssignments is this service's implementation of the DeleteAssignments gRPC method
|
||||
// defined in ../proto/backend.proto
|
||||
func (s *backendAPI) DeleteAssignments(ctx context.Context, a *backend.Roster) (*backend.Result, error) {
|
||||
// TODO: make playerIDs a repeated protobuf message field and iterate over it
|
||||
assignments := strings.Split(a.PlayerIds, " ")
|
||||
|
||||
// Create context for tagging OpenCensus metrics.
|
||||
funcName := "DeleteAssignments"
|
||||
fnCtx, _ := tag.New(ctx, tag.Insert(KeyMethod, funcName))
|
||||
|
||||
beLog = beLog.WithFields(log.Fields{"func": funcName})
|
||||
beLog.WithFields(log.Fields{
|
||||
"numAssignments": len(assignments),
|
||||
}).Info("gRPC call executing")
|
||||
|
||||
// TODO: relocate this redis functionality to a module
|
||||
redisConn := s.pool.Get()
|
||||
defer redisConn.Close()
|
||||
|
||||
// Remove player assignments in a transaction
|
||||
redisConn.Send("MULTI")
|
||||
// TODO: make playerIDs a repeated protobuf message field and iterate over it
|
||||
for _, playerID := range assignments {
|
||||
beLog.WithFields(log.Fields{"query": "DEL", "key": playerID}).Debug("Statestorage operation")
|
||||
redisConn.Send("DEL", playerID)
|
||||
}
|
||||
_, err := redisConn.Do("EXEC")
|
||||
|
||||
// Issue encountered
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"component": "statestorage",
|
||||
}).Error("Statestorage error")
|
||||
|
||||
stats.Record(fnCtx, BeGrpcErrors.M(1))
|
||||
stats.Record(fnCtx, BeAssignmentDeletionFailures.M(int64(len(assignments))))
|
||||
return &backend.Result{Success: false, Error: err.Error()}, err
|
||||
}
|
||||
|
||||
// Success!
|
||||
stats.Record(fnCtx, BeGrpcRequests.M(1))
|
||||
stats.Record(fnCtx, BeAssignmentDeletions.M(int64(len(assignments))))
|
||||
return &backend.Result{Success: true, Error: ""}, err
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package apisrv
|
||||
|
||||
import (
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/view"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
// OpenCensus Measures. These are exported as metrics to your monitoring system
|
||||
// https://godoc.org/go.opencensus.io/stats
|
||||
//
|
||||
// When making opencensus stats, the 'name' param, with forward slashes changed
|
||||
// to underscores, is appended to the 'namespace' value passed to the
|
||||
// prometheus exporter to become the Prometheus metric name. You can also look
|
||||
// into having Prometheus rewrite your metric names on scrape.
|
||||
//
|
||||
// For example:
|
||||
// - defining the promethus export namespace "open_match" when instanciating the exporter:
|
||||
// pe, err := promethus.NewExporter(promethus.Options{Namespace: "open_match"})
|
||||
// - and naming the request counter "backend/requests_total":
|
||||
// MGrpcRequests := stats.Int64("backendapi/requests_total", ...
|
||||
// - results in the prometheus metric name:
|
||||
// open_match_backendapi_requests_total
|
||||
// - [note] when using opencensus views to aggregate the metrics into
|
||||
// distribution buckets and such, multiple metrics
|
||||
// will be generated with appended types ("<metric>_bucket",
|
||||
// "<metric>_count", "<metric>_sum", for example)
|
||||
//
|
||||
// In addition, OpenCensus stats propogated to Prometheus have the following
|
||||
// auto-populated labels pulled from kubernetes, which we should avoid to
|
||||
// prevent overloading and having to use the HonorLabels param in Prometheus.
|
||||
//
|
||||
// - Information about the k8s pod being monitored:
|
||||
// "pod" (name of the monitored k8s pod)
|
||||
// "namespace" (k8s namespace of the monitored pod)
|
||||
// - Information about how promethus is gathering the metrics:
|
||||
// "instance" (IP and port number being scraped by prometheus)
|
||||
// "job" (name of the k8s service being scraped by prometheus)
|
||||
// "endpoint" (name of the k8s port in the k8s service being scraped by prometheus)
|
||||
//
|
||||
var (
|
||||
// API instrumentation
|
||||
BeGrpcRequests = stats.Int64("backendapi/requests_total", "Number of requests to the gRPC Backend API endpoints", "1")
|
||||
BeGrpcErrors = stats.Int64("backendapi/errors_total", "Number of errors generated by the gRPC Backend API endpoints", "1")
|
||||
BeGrpcLatencySecs = stats.Float64("backendapi/latency_seconds", "Latency in seconds of the gRPC Backend API endpoints", "1")
|
||||
|
||||
// Logging instrumentation
|
||||
// There's no need to record this measurement directly if you use
|
||||
// the logrus hook provided in metrics/helper.go after instantiating the
|
||||
// logrus instance in your application code.
|
||||
// https://godoc.org/github.com/sirupsen/logrus#LevelHooks
|
||||
BeLogLines = stats.Int64("backendapi/logs_total", "Number of Backend API lines logged", "1")
|
||||
|
||||
// Failure instrumentation
|
||||
BeFailures = stats.Int64("backendapi/failures_total", "Number of Backend API failures", "1")
|
||||
|
||||
// Counting operations
|
||||
BeAssignments = stats.Int64("backendapi/assignments_total", "Number of players assigned to matches", "1")
|
||||
BeAssignmentFailures = stats.Int64("backendapi/assignment/failures_total", "Number of player match assigment failures", "1")
|
||||
BeAssignmentDeletions = stats.Int64("backendapi/assignment/deletions_total", "Number of player match assigment deletions", "1")
|
||||
BeAssignmentDeletionFailures = stats.Int64("backendapi/assignment/deletions/failures_total", "Number of player match assigment deletion failures", "1")
|
||||
)
|
||||
|
||||
var (
|
||||
// KeyMethod is used to tag a measure with the currently running API method.
|
||||
KeyMethod, _ = tag.NewKey("method")
|
||||
// KeySeverity is used to tag a the severity of a log message.
|
||||
KeySeverity, _ = tag.NewKey("severity")
|
||||
)
|
||||
|
||||
var (
|
||||
// Latency in buckets:
|
||||
// [>=0ms, >=25ms, >=50ms, >=75ms, >=100ms, >=200ms, >=400ms, >=600ms, >=800ms, >=1s, >=2s, >=4s, >=6s]
|
||||
latencyDistribution = view.Distribution(0, 25, 50, 75, 100, 200, 400, 600, 800, 1000, 2000, 4000, 6000)
|
||||
)
|
||||
|
||||
// Package metrics provides some convience views.
|
||||
// You need to register the views for the data to actually be collected.
|
||||
// Note: The OpenCensus View 'Description' is exported to Prometheus as the HELP string.
|
||||
// Note: If you get a "Failed to export to Prometheus: inconsistent label
|
||||
// cardinality" error, chances are you forgot to set the tags specified in the
|
||||
// view for a given measure when you tried to do a stats.Record()
|
||||
var (
|
||||
BeLatencyView = &view.View{
|
||||
Name: "backend/latency",
|
||||
Measure: BeGrpcLatencySecs,
|
||||
Description: "The distribution of backend latencies",
|
||||
Aggregation: latencyDistribution,
|
||||
TagKeys: []tag.Key{KeyMethod},
|
||||
}
|
||||
|
||||
BeRequestCountView = &view.View{
|
||||
Name: "backend/grpc/requests",
|
||||
Measure: BeGrpcRequests,
|
||||
Description: "The number of successful backend gRPC requests",
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{KeyMethod},
|
||||
}
|
||||
|
||||
BeErrorCountView = &view.View{
|
||||
Name: "backend/grpc/errors",
|
||||
Measure: BeGrpcErrors,
|
||||
Description: "The number of gRPC errors",
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{KeyMethod},
|
||||
}
|
||||
|
||||
BeLogCountView = &view.View{
|
||||
Name: "log_lines/total",
|
||||
Measure: BeLogLines,
|
||||
Description: "The number of lines logged",
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{KeySeverity},
|
||||
}
|
||||
|
||||
BeFailureCountView = &view.View{
|
||||
Name: "failures",
|
||||
Measure: BeFailures,
|
||||
Description: "The number of failures",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
BeAssignmentCountView = &view.View{
|
||||
Name: "backend/assignments",
|
||||
Measure: BeAssignments,
|
||||
Description: "The number of successful player match assignments",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
BeAssignmentFailureCountView = &view.View{
|
||||
Name: "backend/assignments/failures",
|
||||
Measure: BeAssignmentFailures,
|
||||
Description: "The number of player match assignment failures",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
BeAssignmentDeletionCountView = &view.View{
|
||||
Name: "backend/assignments/deletions",
|
||||
Measure: BeAssignmentDeletions,
|
||||
Description: "The number of successful player match assignments",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
BeAssignmentDeletionFailureCountView = &view.View{
|
||||
Name: "backend/assignments/deletions/failures",
|
||||
Measure: BeAssignmentDeletionFailures,
|
||||
Description: "The number of player match assignment failures",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultBackendAPIViews are the default backend API OpenCensus measure views.
|
||||
var DefaultBackendAPIViews = []*view.View{
|
||||
BeLatencyView,
|
||||
BeRequestCountView,
|
||||
BeErrorCountView,
|
||||
BeLogCountView,
|
||||
BeFailureCountView,
|
||||
BeAssignmentCountView,
|
||||
BeAssignmentFailureCountView,
|
||||
BeAssignmentDeletionCountView,
|
||||
BeAssignmentDeletionFailureCountView,
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
/*
|
||||
BackendAPI contains the unique files required to run the API endpoints for
|
||||
Open Match's backend. It is assumed you'll either integrate calls to these
|
||||
endpoints directly into your dedicated game server (simple use case), or call
|
||||
these endpoints from other, established services in your infrastructure (more
|
||||
complicated use cases).
|
||||
|
||||
Note that the main package for backendapi does very little except read the
|
||||
config and set up logging and metrics, then start the server. Almost all the
|
||||
work is being done by backendapi/apisrv, which implements the gRPC server
|
||||
defined in the backendapi/proto/backend.pb.go file.
|
||||
*/
|
||||
|
||||
package main
|
@ -1,105 +0,0 @@
|
||||
/*
|
||||
This application handles all the startup and connection scaffolding for
|
||||
running a gRPC server serving the APIService as defined in proto/backend.proto
|
||||
|
||||
All the actual important bits are in the API Server source code: apisrv/apisrv.go
|
||||
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/GoogleCloudPlatform/open-match/cmd/backendapi/apisrv"
|
||||
"github.com/GoogleCloudPlatform/open-match/config"
|
||||
"github.com/GoogleCloudPlatform/open-match/internal/metrics"
|
||||
redishelpers "github.com/GoogleCloudPlatform/open-match/internal/statestorage/redis"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"go.opencensus.io/plugin/ocgrpc"
|
||||
)
|
||||
|
||||
var (
|
||||
// Logrus structured logging setup
|
||||
beLogFields = log.Fields{
|
||||
"app": "openmatch",
|
||||
"component": "backend",
|
||||
"caller": "backendapi/main.go",
|
||||
}
|
||||
beLog = log.WithFields(beLogFields)
|
||||
|
||||
// Viper config management setup
|
||||
cfg = viper.New()
|
||||
err = errors.New("")
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Logrus structured logging initialization
|
||||
// Add a hook to the logger to auto-count log lines for metrics output thru OpenCensus
|
||||
log.AddHook(metrics.NewHook(apisrv.BeLogLines, apisrv.KeySeverity))
|
||||
|
||||
// Viper config management initialization
|
||||
cfg, err = config.Read()
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
}).Error("Unable to load config file")
|
||||
}
|
||||
|
||||
if cfg.GetBool("debug") == true {
|
||||
log.SetLevel(log.DebugLevel) // debug only, verbose - turn off in production!
|
||||
beLog.Warn("Debug logging configured. Not recommended for production!")
|
||||
}
|
||||
|
||||
// Configure OpenCensus exporter to Prometheus
|
||||
// metrics.ConfigureOpenCensusPrometheusExporter expects that every OpenCensus view you
|
||||
// want to register is in an array, so append any views you want from other
|
||||
// packages to a single array here.
|
||||
ocServerViews := apisrv.DefaultBackendAPIViews // BackendAPI OpenCensus views.
|
||||
ocServerViews = append(ocServerViews, ocgrpc.DefaultServerViews...) // gRPC OpenCensus views.
|
||||
ocServerViews = append(ocServerViews, config.CfgVarCountView) // config loader view.
|
||||
// Waiting on https://github.com/opencensus-integrations/redigo/pull/1
|
||||
// ocServerViews = append(ocServerViews, redis.ObservabilityMetricViews...) // redis OpenCensus views.
|
||||
beLog.WithFields(log.Fields{"viewscount": len(ocServerViews)}).Info("Loaded OpenCensus views")
|
||||
metrics.ConfigureOpenCensusPrometheusExporter(cfg, ocServerViews)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Connect to redis
|
||||
pool := redishelpers.ConnectionPool(cfg)
|
||||
defer pool.Close()
|
||||
|
||||
// Instantiate the gRPC server with the connections we've made
|
||||
beLog.WithFields(log.Fields{"testfield": "test"}).Info("Attempting to start gRPC server")
|
||||
srv := apisrv.New(cfg, pool)
|
||||
|
||||
// Run the gRPC server
|
||||
err := srv.Open()
|
||||
if err != nil {
|
||||
beLog.WithFields(log.Fields{"error": err.Error()}).Fatal("Failed to start gRPC server")
|
||||
}
|
||||
|
||||
// Exit when we see a signal
|
||||
terminate := make(chan os.Signal, 1)
|
||||
signal.Notify(terminate, os.Interrupt)
|
||||
<-terminate
|
||||
beLog.Info("Shutting down gRPC server")
|
||||
}
|
@ -1 +0,0 @@
|
||||
../../config/matchmaker_config.json
|
@ -1,482 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
/*
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: backend.proto
|
||||
|
||||
/*
|
||||
Package backend is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
backend.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Profile
|
||||
MatchObject
|
||||
Result
|
||||
Roster
|
||||
ConnectionInfo
|
||||
Assignments
|
||||
*/
|
||||
package backend
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Data structure for a profile to pass to the matchmaking function.
|
||||
type Profile struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Properties string `protobuf:"bytes,2,opt,name=properties" json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Profile) Reset() { *m = Profile{} }
|
||||
func (m *Profile) String() string { return proto.CompactTextString(m) }
|
||||
func (*Profile) ProtoMessage() {}
|
||||
func (*Profile) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *Profile) GetId() string {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Profile) GetProperties() string {
|
||||
if m != nil {
|
||||
return m.Properties
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Data structure for all the properties of a match.
|
||||
type MatchObject struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Properties string `protobuf:"bytes,2,opt,name=properties" json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
func (m *MatchObject) Reset() { *m = MatchObject{} }
|
||||
func (m *MatchObject) String() string { return proto.CompactTextString(m) }
|
||||
func (*MatchObject) ProtoMessage() {}
|
||||
func (*MatchObject) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *MatchObject) GetId() string {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *MatchObject) GetProperties() string {
|
||||
if m != nil {
|
||||
return m.Properties
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Simple message to return success/failure and error status.
|
||||
type Result struct {
|
||||
Success bool `protobuf:"varint,1,opt,name=success" json:"success,omitempty"`
|
||||
Error string `protobuf:"bytes,2,opt,name=error" json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Result) Reset() { *m = Result{} }
|
||||
func (m *Result) String() string { return proto.CompactTextString(m) }
|
||||
func (*Result) ProtoMessage() {}
|
||||
func (*Result) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func (m *Result) GetSuccess() bool {
|
||||
if m != nil {
|
||||
return m.Success
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Result) GetError() string {
|
||||
if m != nil {
|
||||
return m.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Data structure to hold a list of players in a match.
|
||||
type Roster struct {
|
||||
PlayerIds string `protobuf:"bytes,1,opt,name=player_ids,json=playerIds" json:"player_ids,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Roster) Reset() { *m = Roster{} }
|
||||
func (m *Roster) String() string { return proto.CompactTextString(m) }
|
||||
func (*Roster) ProtoMessage() {}
|
||||
func (*Roster) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
func (m *Roster) GetPlayerIds() string {
|
||||
if m != nil {
|
||||
return m.PlayerIds
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Simple message used to pass the connection string for the DGS to the player.
|
||||
type ConnectionInfo struct {
|
||||
ConnectionString string `protobuf:"bytes,1,opt,name=connection_string,json=connectionString" json:"connection_string,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ConnectionInfo) Reset() { *m = ConnectionInfo{} }
|
||||
func (m *ConnectionInfo) String() string { return proto.CompactTextString(m) }
|
||||
func (*ConnectionInfo) ProtoMessage() {}
|
||||
func (*ConnectionInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
|
||||
func (m *ConnectionInfo) GetConnectionString() string {
|
||||
if m != nil {
|
||||
return m.ConnectionString
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Assignments struct {
|
||||
Roster *Roster `protobuf:"bytes,1,opt,name=roster" json:"roster,omitempty"`
|
||||
ConnectionInfo *ConnectionInfo `protobuf:"bytes,2,opt,name=connection_info,json=connectionInfo" json:"connection_info,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Assignments) Reset() { *m = Assignments{} }
|
||||
func (m *Assignments) String() string { return proto.CompactTextString(m) }
|
||||
func (*Assignments) ProtoMessage() {}
|
||||
func (*Assignments) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||
|
||||
func (m *Assignments) GetRoster() *Roster {
|
||||
if m != nil {
|
||||
return m.Roster
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Assignments) GetConnectionInfo() *ConnectionInfo {
|
||||
if m != nil {
|
||||
return m.ConnectionInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Profile)(nil), "Profile")
|
||||
proto.RegisterType((*MatchObject)(nil), "MatchObject")
|
||||
proto.RegisterType((*Result)(nil), "Result")
|
||||
proto.RegisterType((*Roster)(nil), "Roster")
|
||||
proto.RegisterType((*ConnectionInfo)(nil), "ConnectionInfo")
|
||||
proto.RegisterType((*Assignments)(nil), "Assignments")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for API service
|
||||
|
||||
type APIClient interface {
|
||||
// Calls to ask the matchmaker to run a matchmaking function.
|
||||
//
|
||||
// Run MMF once. Return a matchobject that fits this profile.
|
||||
CreateMatch(ctx context.Context, in *Profile, opts ...grpc.CallOption) (*MatchObject, error)
|
||||
// Continually run MMF and stream matchobjects that fit this profile until
|
||||
// client closes the connection.
|
||||
ListMatches(ctx context.Context, in *Profile, opts ...grpc.CallOption) (API_ListMatchesClient, error)
|
||||
// Delete a matchobject from state storage manually. (Matchobjects in state
|
||||
// storage will also automatically expire after a while)
|
||||
DeleteMatch(ctx context.Context, in *MatchObject, opts ...grpc.CallOption) (*Result, error)
|
||||
// Call that manage communication of DGS connection info to players.
|
||||
//
|
||||
// Write the DGS connection info for the list of players in the
|
||||
// Assignments.roster to state storage, so that info can be read by the game
|
||||
// client(s).
|
||||
CreateAssignments(ctx context.Context, in *Assignments, opts ...grpc.CallOption) (*Result, error)
|
||||
// Remove DGS connection info for the list of players in the Roster from
|
||||
// state storage.
|
||||
DeleteAssignments(ctx context.Context, in *Roster, opts ...grpc.CallOption) (*Result, error)
|
||||
}
|
||||
|
||||
type aPIClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewAPIClient(cc *grpc.ClientConn) APIClient {
|
||||
return &aPIClient{cc}
|
||||
}
|
||||
|
||||
func (c *aPIClient) CreateMatch(ctx context.Context, in *Profile, opts ...grpc.CallOption) (*MatchObject, error) {
|
||||
out := new(MatchObject)
|
||||
err := grpc.Invoke(ctx, "/API/CreateMatch", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) ListMatches(ctx context.Context, in *Profile, opts ...grpc.CallOption) (API_ListMatchesClient, error) {
|
||||
stream, err := grpc.NewClientStream(ctx, &_API_serviceDesc.Streams[0], c.cc, "/API/ListMatches", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &aPIListMatchesClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type API_ListMatchesClient interface {
|
||||
Recv() (*MatchObject, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type aPIListMatchesClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *aPIListMatchesClient) Recv() (*MatchObject, error) {
|
||||
m := new(MatchObject)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) DeleteMatch(ctx context.Context, in *MatchObject, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/DeleteMatch", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) CreateAssignments(ctx context.Context, in *Assignments, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/CreateAssignments", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) DeleteAssignments(ctx context.Context, in *Roster, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/DeleteAssignments", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for API service
|
||||
|
||||
type APIServer interface {
|
||||
// Calls to ask the matchmaker to run a matchmaking function.
|
||||
//
|
||||
// Run MMF once. Return a matchobject that fits this profile.
|
||||
CreateMatch(context.Context, *Profile) (*MatchObject, error)
|
||||
// Continually run MMF and stream matchobjects that fit this profile until
|
||||
// client closes the connection.
|
||||
ListMatches(*Profile, API_ListMatchesServer) error
|
||||
// Delete a matchobject from state storage manually. (Matchobjects in state
|
||||
// storage will also automatically expire after a while)
|
||||
DeleteMatch(context.Context, *MatchObject) (*Result, error)
|
||||
// Call that manage communication of DGS connection info to players.
|
||||
//
|
||||
// Write the DGS connection info for the list of players in the
|
||||
// Assignments.roster to state storage, so that info can be read by the game
|
||||
// client(s).
|
||||
CreateAssignments(context.Context, *Assignments) (*Result, error)
|
||||
// Remove DGS connection info for the list of players in the Roster from
|
||||
// state storage.
|
||||
DeleteAssignments(context.Context, *Roster) (*Result, error)
|
||||
}
|
||||
|
||||
func RegisterAPIServer(s *grpc.Server, srv APIServer) {
|
||||
s.RegisterService(&_API_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _API_CreateMatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Profile)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).CreateMatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/CreateMatch",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).CreateMatch(ctx, req.(*Profile))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_ListMatches_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(Profile)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(APIServer).ListMatches(m, &aPIListMatchesServer{stream})
|
||||
}
|
||||
|
||||
type API_ListMatchesServer interface {
|
||||
Send(*MatchObject) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type aPIListMatchesServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *aPIListMatchesServer) Send(m *MatchObject) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _API_DeleteMatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MatchObject)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).DeleteMatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/DeleteMatch",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).DeleteMatch(ctx, req.(*MatchObject))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_CreateAssignments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Assignments)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).CreateAssignments(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/CreateAssignments",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).CreateAssignments(ctx, req.(*Assignments))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_DeleteAssignments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Roster)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).DeleteAssignments(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/DeleteAssignments",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).DeleteAssignments(ctx, req.(*Roster))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _API_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "API",
|
||||
HandlerType: (*APIServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "CreateMatch",
|
||||
Handler: _API_CreateMatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteMatch",
|
||||
Handler: _API_DeleteMatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateAssignments",
|
||||
Handler: _API_CreateAssignments_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteAssignments",
|
||||
Handler: _API_DeleteAssignments_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "ListMatches",
|
||||
Handler: _API_ListMatches_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "backend.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("backend.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 344 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xcf, 0x4e, 0xc2, 0x40,
|
||||
0x10, 0xc6, 0x29, 0xc6, 0x16, 0x66, 0x11, 0x64, 0xe3, 0x81, 0x90, 0xf8, 0x27, 0x3d, 0x88, 0x46,
|
||||
0xb3, 0x31, 0x78, 0xc1, 0x83, 0x07, 0x82, 0x17, 0x12, 0x8d, 0xa4, 0x3e, 0x00, 0x29, 0xdb, 0x01,
|
||||
0x56, 0xeb, 0x6e, 0xb3, 0xbb, 0x1c, 0x7c, 0x53, 0x1f, 0xc7, 0xb8, 0x2d, 0xba, 0x1c, 0x3c, 0x78,
|
||||
0x9c, 0x5f, 0xbf, 0x6f, 0xe6, 0xeb, 0xcc, 0xc2, 0xc1, 0x22, 0xe5, 0x6f, 0x28, 0x33, 0x56, 0x68,
|
||||
0x65, 0x55, 0x7c, 0x07, 0xd1, 0x4c, 0xab, 0xa5, 0xc8, 0x91, 0xb6, 0xa1, 0x2e, 0xb2, 0x5e, 0x70,
|
||||
0x16, 0x5c, 0x34, 0x93, 0xba, 0xc8, 0xe8, 0x09, 0x40, 0xa1, 0x55, 0x81, 0xda, 0x0a, 0x34, 0xbd,
|
||||
0xba, 0xe3, 0x1e, 0x89, 0xef, 0x81, 0x3c, 0xa5, 0x96, 0xaf, 0x9f, 0x17, 0xaf, 0xc8, 0xed, 0xbf,
|
||||
0xed, 0x23, 0x08, 0x13, 0x34, 0x9b, 0xdc, 0xd2, 0x1e, 0x44, 0x66, 0xc3, 0x39, 0x1a, 0xe3, 0xec,
|
||||
0x8d, 0x64, 0x5b, 0xd2, 0x23, 0xd8, 0x47, 0xad, 0x95, 0xae, 0xec, 0x65, 0x11, 0x0f, 0x20, 0x4c,
|
||||
0x94, 0xb1, 0xa8, 0xe9, 0x31, 0x40, 0x91, 0xa7, 0x1f, 0xa8, 0xe7, 0x22, 0x33, 0xd5, 0xec, 0x66,
|
||||
0x49, 0xa6, 0xd9, 0x77, 0xc2, 0xf6, 0x44, 0x49, 0x89, 0xdc, 0x0a, 0x25, 0xa7, 0x72, 0xa9, 0xe8,
|
||||
0x15, 0x74, 0xf9, 0x0f, 0x99, 0x1b, 0xab, 0x85, 0x5c, 0x55, 0xbe, 0xc3, 0xdf, 0x0f, 0x2f, 0x8e,
|
||||
0xc7, 0x6b, 0x20, 0x63, 0x63, 0xc4, 0x4a, 0xbe, 0xa3, 0xb4, 0x86, 0x9e, 0x42, 0xa8, 0xdd, 0x58,
|
||||
0x67, 0x20, 0xc3, 0x88, 0x95, 0x29, 0x92, 0x0a, 0xd3, 0x11, 0x74, 0xbc, 0xe6, 0x42, 0x2e, 0x95,
|
||||
0xcb, 0x4d, 0x86, 0x1d, 0xb6, 0x1b, 0x23, 0x69, 0xf3, 0x9d, 0x7a, 0xf8, 0x19, 0xc0, 0xde, 0x78,
|
||||
0x36, 0xa5, 0x03, 0x20, 0x13, 0x8d, 0xa9, 0x45, 0xb7, 0x58, 0xda, 0x60, 0xd5, 0x6d, 0xfa, 0x2d,
|
||||
0xe6, 0xad, 0x3a, 0xae, 0xd1, 0x4b, 0x20, 0x8f, 0xc2, 0x58, 0x07, 0xd1, 0xfc, 0x2d, 0xbc, 0x09,
|
||||
0xe8, 0x39, 0x90, 0x07, 0xcc, 0x71, 0xdb, 0x73, 0x47, 0xd0, 0x8f, 0x58, 0x79, 0x83, 0xb8, 0x46,
|
||||
0xaf, 0xa1, 0x5b, 0xce, 0xf6, 0xff, 0xb9, 0xc5, 0xbc, 0xca, 0x57, 0x0f, 0xa0, 0x5b, 0x76, 0xf5,
|
||||
0xd5, 0xdb, 0x8d, 0x78, 0xc2, 0x45, 0xe8, 0xde, 0xd9, 0xed, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0xf2, 0x23, 0x14, 0x36, 0x78, 0x02, 0x00, 0x00,
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
/*
|
||||
backend is a package compiled from the protobuffer in <REPO_ROOT>/api/protobuf-spec/backend.proto. It is auto-generated and shouldn't be edited.
|
||||
*/
|
||||
package backend
|
24
cmd/default-evaluator/main.go
Normal file
24
cmd/default-evaluator/main.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app/evaluator/defaulteval"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appmain.RunApplication("evaluator", defaulteval.BindService)
|
||||
}
|
31
cmd/demo-first-match/main.go
Normal file
31
cmd/demo-first-match/main.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/examples/demo"
|
||||
"open-match.dev/open-match/examples/demo/components"
|
||||
"open-match.dev/open-match/examples/demo/components/clients"
|
||||
"open-match.dev/open-match/examples/demo/components/director"
|
||||
"open-match.dev/open-match/examples/demo/components/uptime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
demo.Run(map[string]func(*components.DemoShared){
|
||||
"uptime": uptime.Run,
|
||||
"clients": clients.Run,
|
||||
"director": director.Run,
|
||||
})
|
||||
}
|
25
cmd/frontend/frontend.go
Normal file
25
cmd/frontend/frontend.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package main is the frontend service for Open Match.
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app/frontend"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appmain.RunApplication("frontend", frontend.BindService)
|
||||
}
|
@ -1,300 +0,0 @@
|
||||
/*
|
||||
package apisrv provides an implementation of the gRPC server defined in ../proto/frontend.proto.
|
||||
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
package apisrv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
frontend "github.com/GoogleCloudPlatform/open-match/cmd/frontendapi/proto"
|
||||
"github.com/GoogleCloudPlatform/open-match/internal/metrics"
|
||||
playerq "github.com/GoogleCloudPlatform/open-match/internal/statestorage/redis/playerq"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"go.opencensus.io/plugin/ocgrpc"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Logrus structured logging setup
|
||||
var (
|
||||
feLogFields = log.Fields{
|
||||
"app": "openmatch",
|
||||
"component": "frontend",
|
||||
"caller": "frontendapi/apisrv/apisrv.go",
|
||||
}
|
||||
feLog = log.WithFields(feLogFields)
|
||||
)
|
||||
|
||||
// FrontendAPI implements frontend.ApiServer, the server generated by compiling
|
||||
// the protobuf, by fulfilling the frontend.APIClient interface.
|
||||
type FrontendAPI struct {
|
||||
grpc *grpc.Server
|
||||
cfg *viper.Viper
|
||||
pool *redis.Pool
|
||||
}
|
||||
type frontendAPI FrontendAPI
|
||||
|
||||
// New returns an instantiated srvice
|
||||
func New(cfg *viper.Viper, pool *redis.Pool) *FrontendAPI {
|
||||
s := FrontendAPI{
|
||||
pool: pool,
|
||||
grpc: grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{})),
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
// Add a hook to the logger to auto-count log lines for metrics output thru OpenCensus
|
||||
log.AddHook(metrics.NewHook(FeLogLines, KeySeverity))
|
||||
|
||||
// Register gRPC server
|
||||
frontend.RegisterAPIServer(s.grpc, (*frontendAPI)(&s))
|
||||
feLog.Info("Successfully registered gRPC server")
|
||||
return &s
|
||||
}
|
||||
|
||||
// Open opens the api grpc service, starting it listening on the configured port.
|
||||
func (s *FrontendAPI) Open() error {
|
||||
ln, err := net.Listen("tcp", ":"+s.cfg.GetString("api.frontend.port"))
|
||||
if err != nil {
|
||||
feLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"port": s.cfg.GetInt("api.frontend.port"),
|
||||
}).Error("net.Listen() error")
|
||||
return err
|
||||
}
|
||||
feLog.WithFields(log.Fields{"port": s.cfg.GetInt("api.frontend.port")}).Info("TCP net listener initialized")
|
||||
|
||||
go func() {
|
||||
err := s.grpc.Serve(ln)
|
||||
if err != nil {
|
||||
feLog.WithFields(log.Fields{"error": err.Error()}).Error("gRPC serve() error")
|
||||
}
|
||||
feLog.Info("serving gRPC endpoints")
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateRequest is this service's implementation of the CreateRequest gRPC method // defined in ../proto/frontend.proto
|
||||
func (s *frontendAPI) CreateRequest(c context.Context, g *frontend.Group) (*frontend.Result, error) {
|
||||
|
||||
// Get redis connection from pool
|
||||
redisConn := s.pool.Get()
|
||||
defer redisConn.Close()
|
||||
|
||||
// Create context for tagging OpenCensus metrics.
|
||||
funcName := "CreateRequest"
|
||||
fnCtx, _ := tag.New(c, tag.Insert(KeyMethod, funcName))
|
||||
|
||||
// Write group
|
||||
// TODO: Remove playerq module and just use redishelper module once
|
||||
// indexing has its own implementation
|
||||
err := playerq.Create(redisConn, g.Id, g.Properties)
|
||||
|
||||
if err != nil {
|
||||
feLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"component": "statestorage",
|
||||
}).Error("State storage error")
|
||||
|
||||
stats.Record(fnCtx, FeGrpcErrors.M(1))
|
||||
return &frontend.Result{Success: false, Error: err.Error()}, err
|
||||
}
|
||||
|
||||
stats.Record(fnCtx, FeGrpcRequests.M(1))
|
||||
return &frontend.Result{Success: true, Error: ""}, err
|
||||
|
||||
}
|
||||
|
||||
// DeleteRequest is this service's implementation of the DeleteRequest gRPC method defined in
|
||||
// frontendapi/proto/frontend.proto
|
||||
func (s *frontendAPI) DeleteRequest(c context.Context, g *frontend.Group) (*frontend.Result, error) {
|
||||
// Get redis connection from pool
|
||||
redisConn := s.pool.Get()
|
||||
defer redisConn.Close()
|
||||
|
||||
// Create context for tagging OpenCensus metrics.
|
||||
funcName := "DeleteRequest"
|
||||
fnCtx, _ := tag.New(c, tag.Insert(KeyMethod, funcName))
|
||||
|
||||
// Write group
|
||||
err := playerq.Delete(redisConn, g.Id)
|
||||
if err != nil {
|
||||
feLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"component": "statestorage",
|
||||
}).Error("State storage error")
|
||||
|
||||
stats.Record(fnCtx, FeGrpcErrors.M(1))
|
||||
return &frontend.Result{Success: false, Error: err.Error()}, err
|
||||
}
|
||||
|
||||
stats.Record(fnCtx, FeGrpcRequests.M(1))
|
||||
return &frontend.Result{Success: true, Error: ""}, err
|
||||
|
||||
}
|
||||
|
||||
// GetAssignment is this service's implementation of the GetAssignment gRPC method defined in
|
||||
// frontendapi/proto/frontend.proto
|
||||
func (s *frontendAPI) GetAssignment(c context.Context, p *frontend.PlayerId) (*frontend.ConnectionInfo, error) {
|
||||
// Get cancellable context
|
||||
ctx, cancel := context.WithCancel(c)
|
||||
defer cancel()
|
||||
|
||||
// Create context for tagging OpenCensus metrics.
|
||||
funcName := "GetAssignment"
|
||||
fnCtx, _ := tag.New(ctx, tag.Insert(KeyMethod, funcName))
|
||||
|
||||
// get and return connection string
|
||||
var connString string
|
||||
watchChan := s.watcher(ctx, s.pool, p.Id) // watcher() runs the appropriate Redis commands.
|
||||
|
||||
select {
|
||||
case <-time.After(30 * time.Second): // TODO: Make this configurable.
|
||||
err := errors.New("did not see matchmaking results in redis before timeout")
|
||||
// TODO:Timeout: deal with the fallout
|
||||
// When there is a timeout, need to send a stop to the watch channel.
|
||||
// cancelling ctx isn't doing it.
|
||||
//cancel()
|
||||
feLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"component": "statestorage",
|
||||
"playerid": p.Id,
|
||||
}).Error("State storage error")
|
||||
|
||||
errTag, _ := tag.NewKey("errtype")
|
||||
fnCtx, _ := tag.New(ctx, tag.Insert(errTag, "watch_timeout"))
|
||||
stats.Record(fnCtx, FeGrpcErrors.M(1))
|
||||
return &frontend.ConnectionInfo{ConnectionString: ""}, err
|
||||
|
||||
case connString = <-watchChan:
|
||||
feLog.Debug(p.Id, "connString:", connString)
|
||||
}
|
||||
|
||||
stats.Record(fnCtx, FeGrpcRequests.M(1))
|
||||
return &frontend.ConnectionInfo{ConnectionString: connString}, nil
|
||||
}
|
||||
|
||||
// DeleteAssignment is this service's implementation of the DeleteAssignment gRPC method defined in
|
||||
// frontendapi/proto/frontend.proto
|
||||
func (s *frontendAPI) DeleteAssignment(c context.Context, p *frontend.PlayerId) (*frontend.Result, error) {
|
||||
|
||||
// Get redis connection from pool
|
||||
redisConn := s.pool.Get()
|
||||
defer redisConn.Close()
|
||||
|
||||
// Create context for tagging OpenCensus metrics.
|
||||
funcName := "DeleteAssignment"
|
||||
fnCtx, _ := tag.New(c, tag.Insert(KeyMethod, funcName))
|
||||
|
||||
// Write group
|
||||
err := playerq.Delete(redisConn, p.Id)
|
||||
if err != nil {
|
||||
feLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"component": "statestorage",
|
||||
}).Error("State storage error")
|
||||
|
||||
stats.Record(fnCtx, FeGrpcErrors.M(1))
|
||||
return &frontend.Result{Success: false, Error: err.Error()}, err
|
||||
}
|
||||
|
||||
stats.Record(fnCtx, FeGrpcRequests.M(1))
|
||||
return &frontend.Result{Success: true, Error: ""}, err
|
||||
|
||||
}
|
||||
|
||||
//TODO: Everything below this line will be moved to the redis statestorage library
|
||||
// in an upcoming version.
|
||||
// ================================================
|
||||
|
||||
// watcher makes a channel and returns it immediately. It also launches an
|
||||
// asynchronous goroutine that watches a redis key and returns the value of
|
||||
// the 'connstring' field of that key once it exists on the channel.
|
||||
//
|
||||
// The pattern for this function is from 'Go Concurrency Patterns', it is a function
|
||||
// that wraps a closure goroutine, and returns a channel.
|
||||
// reference: https://talks.golang.org/2012/concurrency.slide#25
|
||||
func (s *frontendAPI) watcher(ctx context.Context, pool *redis.Pool, key string) <-chan string {
|
||||
// Add the key as a field to all logs for the execution of this function.
|
||||
feLog = feLog.WithFields(log.Fields{"key": key})
|
||||
feLog.Debug("Watching key in statestorage for changes")
|
||||
|
||||
watchChan := make(chan string)
|
||||
|
||||
go func() {
|
||||
// var declaration
|
||||
var results string
|
||||
var err = errors.New("haven't queried Redis yet")
|
||||
|
||||
// Loop, querying redis until this key has a value
|
||||
for err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Cleanup
|
||||
close(watchChan)
|
||||
return
|
||||
default:
|
||||
results, err = s.retrieveConnstring(ctx, pool, key, s.cfg.GetString("jsonkeys.connstring"))
|
||||
if err != nil {
|
||||
time.Sleep(5 * time.Second) // TODO: exp bo + jitter
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return value retreived from Redis asynchonously and tell calling function we're done
|
||||
feLog.Debug("Statestorage watched record update detected")
|
||||
watchChan <- results
|
||||
close(watchChan)
|
||||
}()
|
||||
|
||||
return watchChan
|
||||
}
|
||||
|
||||
// retrieveConnstring is a concurrent-safe, context-aware redis HGET of the 'connstring' fieldin the input key
|
||||
// TODO: This will be moved to the redis statestorage module.
|
||||
func (s *frontendAPI) retrieveConnstring(ctx context.Context, pool *redis.Pool, key string, field string) (string, error) {
|
||||
|
||||
// Add the key as a field to all logs for the execution of this function.
|
||||
feLog = feLog.WithFields(log.Fields{"key": key})
|
||||
|
||||
cmd := "HGET"
|
||||
feLog.WithFields(log.Fields{"query": cmd}).Debug("Statestorage operation")
|
||||
|
||||
// Get a connection to redis
|
||||
redisConn, err := pool.GetContext(ctx)
|
||||
defer redisConn.Close()
|
||||
|
||||
// Encountered an issue getting a connection from the pool.
|
||||
if err != nil {
|
||||
feLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"query": cmd}).Error("Statestorage connection error")
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Run redis query and return
|
||||
return redis.String(redisConn.Do("HGET", key, field))
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package apisrv
|
||||
|
||||
import (
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/view"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
// OpenCensus Measures. These are exported as metrics to your monitoring system
|
||||
// https://godoc.org/go.opencensus.io/stats
|
||||
//
|
||||
// When making opencensus stats, the 'name' param, with forward slashes changed
|
||||
// to underscores, is appended to the 'namespace' value passed to the
|
||||
// prometheus exporter to become the Prometheus metric name. You can also look
|
||||
// into having Prometheus rewrite your metric names on scrape.
|
||||
//
|
||||
// For example:
|
||||
// - defining the promethus export namespace "open_match" when instanciating the exporter:
|
||||
// pe, err := promethus.NewExporter(promethus.Options{Namespace: "open_match"})
|
||||
// - and naming the request counter "frontend/requests_total":
|
||||
// MGrpcRequests := stats.Int64("frontendapi/requests_total", ...
|
||||
// - results in the prometheus metric name:
|
||||
// open_match_frontendapi_requests_total
|
||||
// - [note] when using opencensus views to aggregate the metrics into
|
||||
// distribution buckets and such, multiple metrics
|
||||
// will be generated with appended types ("<metric>_bucket",
|
||||
// "<metric>_count", "<metric>_sum", for example)
|
||||
//
|
||||
// In addition, OpenCensus stats propogated to Prometheus have the following
|
||||
// auto-populated labels pulled from kubernetes, which we should avoid to
|
||||
// prevent overloading and having to use the HonorLabels param in Prometheus.
|
||||
//
|
||||
// - Information about the k8s pod being monitored:
|
||||
// "pod" (name of the monitored k8s pod)
|
||||
// "namespace" (k8s namespace of the monitored pod)
|
||||
// - Information about how promethus is gathering the metrics:
|
||||
// "instance" (IP and port number being scraped by prometheus)
|
||||
// "job" (name of the k8s service being scraped by prometheus)
|
||||
// "endpoint" (name of the k8s port in the k8s service being scraped by prometheus)
|
||||
//
|
||||
var (
|
||||
// API instrumentation
|
||||
FeGrpcRequests = stats.Int64("frontendapi/requests_total", "Number of requests to the gRPC Frontend API endpoints", "1")
|
||||
FeGrpcErrors = stats.Int64("frontendapi/errors_total", "Number of errors generated by the gRPC Frontend API endpoints", "1")
|
||||
FeGrpcLatencySecs = stats.Float64("frontendapi/latency_seconds", "Latency in seconds of the gRPC Frontend API endpoints", "1")
|
||||
|
||||
// Logging instrumentation
|
||||
// There's no need to record this measurement directly if you use
|
||||
// the logrus hook provided in metrics/helper.go after instantiating the
|
||||
// logrus instance in your application code.
|
||||
// https://godoc.org/github.com/sirupsen/logrus#LevelHooks
|
||||
FeLogLines = stats.Int64("frontendapi/logs_total", "Number of Frontend API lines logged", "1")
|
||||
|
||||
// Failure instrumentation
|
||||
FeFailures = stats.Int64("frontendapi/failures_total", "Number of Frontend API failures", "1")
|
||||
)
|
||||
|
||||
var (
|
||||
// KeyMethod is used to tag a measure with the currently running API method.
|
||||
KeyMethod, _ = tag.NewKey("method")
|
||||
KeySeverity, _ = tag.NewKey("severity")
|
||||
)
|
||||
|
||||
var (
|
||||
// Latency in buckets:
|
||||
// [>=0ms, >=25ms, >=50ms, >=75ms, >=100ms, >=200ms, >=400ms, >=600ms, >=800ms, >=1s, >=2s, >=4s, >=6s]
|
||||
latencyDistribution = view.Distribution(0, 25, 50, 75, 100, 200, 400, 600, 800, 1000, 2000, 4000, 6000)
|
||||
)
|
||||
|
||||
// Package metrics provides some convience views.
|
||||
// You need to register the views for the data to actually be collected.
|
||||
// Note: The OpenCensus View 'Description' is exported to Prometheus as the HELP string.
|
||||
// Note: If you get a "Failed to export to Prometheus: inconsistent label
|
||||
// cardinality" error, chances are you forgot to set the tags specified in the
|
||||
// view for a given measure when you tried to do a stats.Record()
|
||||
var (
|
||||
FeLatencyView = &view.View{
|
||||
Name: "frontend/latency",
|
||||
Measure: FeGrpcLatencySecs,
|
||||
Description: "The distribution of frontend latencies",
|
||||
Aggregation: latencyDistribution,
|
||||
TagKeys: []tag.Key{KeyMethod},
|
||||
}
|
||||
|
||||
FeRequestCountView = &view.View{
|
||||
Name: "frontend/grpc/requests",
|
||||
Measure: FeGrpcRequests,
|
||||
Description: "The number of successful frontend gRPC requests",
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{KeyMethod},
|
||||
}
|
||||
|
||||
FeErrorCountView = &view.View{
|
||||
Name: "frontend/grpc/errors",
|
||||
Measure: FeGrpcErrors,
|
||||
Description: "The number of gRPC errors",
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{KeyMethod},
|
||||
}
|
||||
|
||||
FeLogCountView = &view.View{
|
||||
Name: "log_lines/total",
|
||||
Measure: FeLogLines,
|
||||
Description: "The number of lines logged",
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{KeySeverity},
|
||||
}
|
||||
|
||||
FeFailureCountView = &view.View{
|
||||
Name: "failures",
|
||||
Measure: FeFailures,
|
||||
Description: "The number of failures",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultFrontendAPIViews are the default frontend API OpenCensus measure views.
|
||||
var DefaultFrontendAPIViews = []*view.View{
|
||||
FeLatencyView,
|
||||
FeRequestCountView,
|
||||
FeErrorCountView,
|
||||
FeLogCountView,
|
||||
FeFailureCountView,
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
/*
|
||||
FrontendAPI contains the unique files required to run the API endpoints for
|
||||
Open Match's frontend. It is assumed you'll either integrate calls to these
|
||||
endpoints directly into your game client (simple use case), or call these
|
||||
endpoints from other, established platform services in your infrastructure
|
||||
(more complicated use cases).
|
||||
|
||||
Note that the main package for frontendapi does very little except read the
|
||||
config and set up logging and metrics, then start the server. Almost all the
|
||||
work is being done by frontendapi/apisrv, which implements the gRPC server
|
||||
defined in the frontendapi/proto/frontend.pb.go file.
|
||||
*/
|
||||
|
||||
package main
|
@ -1,105 +0,0 @@
|
||||
/*
|
||||
This application handles all the startup and connection scaffolding for
|
||||
running a gRPC server serving the APIService as defined in
|
||||
frontendapi/proto/frontend.pb.go
|
||||
|
||||
All the actual important bits are in the API Server source code: apisrv/apisrv.go
|
||||
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/GoogleCloudPlatform/open-match/cmd/frontendapi/apisrv"
|
||||
"github.com/GoogleCloudPlatform/open-match/config"
|
||||
"github.com/GoogleCloudPlatform/open-match/internal/metrics"
|
||||
redishelpers "github.com/GoogleCloudPlatform/open-match/internal/statestorage/redis"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"go.opencensus.io/plugin/ocgrpc"
|
||||
)
|
||||
|
||||
var (
|
||||
// Logrus structured logging setup
|
||||
feLogFields = log.Fields{
|
||||
"app": "openmatch",
|
||||
"component": "frontend",
|
||||
"caller": "frontendapi/main.go",
|
||||
}
|
||||
feLog = log.WithFields(feLogFields)
|
||||
|
||||
// Viper config management setup
|
||||
cfg = viper.New()
|
||||
err = errors.New("")
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Logrus structured logging initialization
|
||||
// Add a hook to the logger to auto-count log lines for metrics output thru OpenCensus
|
||||
log.AddHook(metrics.NewHook(apisrv.FeLogLines, apisrv.KeySeverity))
|
||||
|
||||
// Viper config management initialization
|
||||
cfg, err = config.Read()
|
||||
if err != nil {
|
||||
feLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
}).Error("Unable to load config file")
|
||||
}
|
||||
|
||||
if cfg.GetBool("debug") == true {
|
||||
log.SetLevel(log.DebugLevel) // debug only, verbose - turn off in production!
|
||||
feLog.Warn("Debug logging configured. Not recommended for production!")
|
||||
}
|
||||
|
||||
// Configure OpenCensus exporter to Prometheus
|
||||
// metrics.ConfigureOpenCensusPrometheusExporter expects that every OpenCensus view you
|
||||
// want to register is in an array, so append any views you want from other
|
||||
// packages to a single array here.
|
||||
ocServerViews := apisrv.DefaultFrontendAPIViews // FrontendAPI OpenCensus views.
|
||||
ocServerViews = append(ocServerViews, ocgrpc.DefaultServerViews...) // gRPC OpenCensus views.
|
||||
ocServerViews = append(ocServerViews, config.CfgVarCountView) // config loader view.
|
||||
// Waiting on https://github.com/opencensus-integrations/redigo/pull/1
|
||||
// ocServerViews = append(ocServerViews, redis.ObservabilityMetricViews...) // redis OpenCensus views.
|
||||
feLog.WithFields(log.Fields{"viewscount": len(ocServerViews)}).Info("Loaded OpenCensus views")
|
||||
metrics.ConfigureOpenCensusPrometheusExporter(cfg, ocServerViews)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Connect to redis
|
||||
pool := redishelpers.ConnectionPool(cfg)
|
||||
defer pool.Close()
|
||||
|
||||
// Instantiate the gRPC server with the connections we've made
|
||||
feLog.WithFields(log.Fields{"testfield": "test"}).Info("Attempting to start gRPC server")
|
||||
srv := apisrv.New(cfg, pool)
|
||||
|
||||
// Run the gRPC server
|
||||
err := srv.Open()
|
||||
if err != nil {
|
||||
feLog.WithFields(log.Fields{"error": err.Error()}).Fatal("Failed to start gRPC server")
|
||||
}
|
||||
|
||||
// Exit when we see a signal
|
||||
terminate := make(chan os.Signal, 1)
|
||||
signal.Notify(terminate, os.Interrupt)
|
||||
<-terminate
|
||||
feLog.Info("Shutting down gRPC server")
|
||||
}
|
@ -1 +0,0 @@
|
||||
../../config/matchmaker_config.json
|
@ -1,4 +0,0 @@
|
||||
/*
|
||||
frontend is a package compiled from the protobuffer in <REPO_ROOT>/api/protobuf-spec/frontend.proto. It is auto-generated and shouldn't be edited.
|
||||
*/
|
||||
package frontend
|
@ -1,335 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: frontend.proto
|
||||
|
||||
/*
|
||||
Package frontend is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
frontend.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Group
|
||||
PlayerId
|
||||
ConnectionInfo
|
||||
Result
|
||||
*/
|
||||
package frontend
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Data structure for a group of players to pass to the matchmaking function.
|
||||
// Obviously, the group can be a group of one!
|
||||
type Group struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Properties string `protobuf:"bytes,2,opt,name=properties" json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Group) Reset() { *m = Group{} }
|
||||
func (m *Group) String() string { return proto.CompactTextString(m) }
|
||||
func (*Group) ProtoMessage() {}
|
||||
func (*Group) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *Group) GetId() string {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Group) GetProperties() string {
|
||||
if m != nil {
|
||||
return m.Properties
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type PlayerId struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (m *PlayerId) Reset() { *m = PlayerId{} }
|
||||
func (m *PlayerId) String() string { return proto.CompactTextString(m) }
|
||||
func (*PlayerId) ProtoMessage() {}
|
||||
func (*PlayerId) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *PlayerId) GetId() string {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Simple message used to pass the connection string for the DGS to the player.
|
||||
type ConnectionInfo struct {
|
||||
ConnectionString string `protobuf:"bytes,1,opt,name=connection_string,json=connectionString" json:"connection_string,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ConnectionInfo) Reset() { *m = ConnectionInfo{} }
|
||||
func (m *ConnectionInfo) String() string { return proto.CompactTextString(m) }
|
||||
func (*ConnectionInfo) ProtoMessage() {}
|
||||
func (*ConnectionInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func (m *ConnectionInfo) GetConnectionString() string {
|
||||
if m != nil {
|
||||
return m.ConnectionString
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Simple message to return success/failure and error status.
|
||||
type Result struct {
|
||||
Success bool `protobuf:"varint,1,opt,name=success" json:"success,omitempty"`
|
||||
Error string `protobuf:"bytes,2,opt,name=error" json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Result) Reset() { *m = Result{} }
|
||||
func (m *Result) String() string { return proto.CompactTextString(m) }
|
||||
func (*Result) ProtoMessage() {}
|
||||
func (*Result) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
func (m *Result) GetSuccess() bool {
|
||||
if m != nil {
|
||||
return m.Success
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Result) GetError() string {
|
||||
if m != nil {
|
||||
return m.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Group)(nil), "Group")
|
||||
proto.RegisterType((*PlayerId)(nil), "PlayerId")
|
||||
proto.RegisterType((*ConnectionInfo)(nil), "ConnectionInfo")
|
||||
proto.RegisterType((*Result)(nil), "Result")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for API service
|
||||
|
||||
type APIClient interface {
|
||||
CreateRequest(ctx context.Context, in *Group, opts ...grpc.CallOption) (*Result, error)
|
||||
DeleteRequest(ctx context.Context, in *Group, opts ...grpc.CallOption) (*Result, error)
|
||||
GetAssignment(ctx context.Context, in *PlayerId, opts ...grpc.CallOption) (*ConnectionInfo, error)
|
||||
DeleteAssignment(ctx context.Context, in *PlayerId, opts ...grpc.CallOption) (*Result, error)
|
||||
}
|
||||
|
||||
type aPIClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewAPIClient(cc *grpc.ClientConn) APIClient {
|
||||
return &aPIClient{cc}
|
||||
}
|
||||
|
||||
func (c *aPIClient) CreateRequest(ctx context.Context, in *Group, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/CreateRequest", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) DeleteRequest(ctx context.Context, in *Group, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/DeleteRequest", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) GetAssignment(ctx context.Context, in *PlayerId, opts ...grpc.CallOption) (*ConnectionInfo, error) {
|
||||
out := new(ConnectionInfo)
|
||||
err := grpc.Invoke(ctx, "/API/GetAssignment", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aPIClient) DeleteAssignment(ctx context.Context, in *PlayerId, opts ...grpc.CallOption) (*Result, error) {
|
||||
out := new(Result)
|
||||
err := grpc.Invoke(ctx, "/API/DeleteAssignment", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for API service
|
||||
|
||||
type APIServer interface {
|
||||
CreateRequest(context.Context, *Group) (*Result, error)
|
||||
DeleteRequest(context.Context, *Group) (*Result, error)
|
||||
GetAssignment(context.Context, *PlayerId) (*ConnectionInfo, error)
|
||||
DeleteAssignment(context.Context, *PlayerId) (*Result, error)
|
||||
}
|
||||
|
||||
func RegisterAPIServer(s *grpc.Server, srv APIServer) {
|
||||
s.RegisterService(&_API_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _API_CreateRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Group)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).CreateRequest(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/CreateRequest",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).CreateRequest(ctx, req.(*Group))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_DeleteRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Group)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).DeleteRequest(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/DeleteRequest",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).DeleteRequest(ctx, req.(*Group))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_GetAssignment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PlayerId)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).GetAssignment(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/GetAssignment",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).GetAssignment(ctx, req.(*PlayerId))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _API_DeleteAssignment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PlayerId)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(APIServer).DeleteAssignment(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/API/DeleteAssignment",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(APIServer).DeleteAssignment(ctx, req.(*PlayerId))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _API_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "API",
|
||||
HandlerType: (*APIServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "CreateRequest",
|
||||
Handler: _API_CreateRequest_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteRequest",
|
||||
Handler: _API_DeleteRequest_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetAssignment",
|
||||
Handler: _API_GetAssignment_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteAssignment",
|
||||
Handler: _API_DeleteAssignment_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "frontend.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("frontend.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 260 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0x41, 0x4b, 0xfb, 0x40,
|
||||
0x10, 0xc5, 0x9b, 0xfc, 0x69, 0xda, 0x0e, 0x34, 0xff, 0xba, 0x78, 0x08, 0x39, 0x88, 0xec, 0xa9,
|
||||
0x20, 0xee, 0x41, 0x0f, 0x7a, 0xf1, 0x50, 0x2a, 0x94, 0xdc, 0x4a, 0xfc, 0x00, 0x52, 0x93, 0x69,
|
||||
0x59, 0x88, 0xbb, 0x71, 0x66, 0x72, 0xf0, 0x0b, 0xf9, 0x39, 0xc5, 0x4d, 0x6b, 0x55, 0xc4, 0xe3,
|
||||
0xfb, 0xed, 0x7b, 0x8f, 0x7d, 0x03, 0xe9, 0x96, 0xbc, 0x13, 0x74, 0xb5, 0x69, 0xc9, 0x8b, 0xd7,
|
||||
0x37, 0x30, 0x5c, 0x91, 0xef, 0x5a, 0x95, 0x42, 0x6c, 0xeb, 0x2c, 0x3a, 0x8f, 0xe6, 0x93, 0x32,
|
||||
0xb6, 0xb5, 0x3a, 0x03, 0x68, 0xc9, 0xb7, 0x48, 0x62, 0x91, 0xb3, 0x38, 0xf0, 0x2f, 0x44, 0xe7,
|
||||
0x30, 0x5e, 0x37, 0x9b, 0x57, 0xa4, 0xa2, 0xfe, 0x99, 0xd5, 0x77, 0x90, 0x2e, 0xbd, 0x73, 0x58,
|
||||
0x89, 0xf5, 0xae, 0x70, 0x5b, 0xaf, 0x2e, 0xe0, 0xa4, 0xfa, 0x24, 0x8f, 0x2c, 0x64, 0xdd, 0x6e,
|
||||
0x1f, 0x98, 0x1d, 0x1f, 0x1e, 0x02, 0xd7, 0xb7, 0x90, 0x94, 0xc8, 0x5d, 0x23, 0x2a, 0x83, 0x11,
|
||||
0x77, 0x55, 0x85, 0xcc, 0xc1, 0x3c, 0x2e, 0x0f, 0x52, 0x9d, 0xc2, 0x10, 0x89, 0x3c, 0xed, 0x7f,
|
||||
0xd6, 0x8b, 0xab, 0xb7, 0x08, 0xfe, 0x2d, 0xd6, 0x85, 0xd2, 0x30, 0x5d, 0x12, 0x6e, 0x04, 0x4b,
|
||||
0x7c, 0xe9, 0x90, 0x45, 0x25, 0x26, 0xac, 0xcc, 0x47, 0xa6, 0x6f, 0xd6, 0x83, 0x0f, 0xcf, 0x3d,
|
||||
0x36, 0xf8, 0xa7, 0xe7, 0x12, 0xa6, 0x2b, 0x94, 0x05, 0xb3, 0xdd, 0xb9, 0x67, 0x74, 0xa2, 0x26,
|
||||
0xe6, 0x30, 0x3a, 0xff, 0x6f, 0xbe, 0x6f, 0xd4, 0x03, 0x35, 0x87, 0x59, 0x5f, 0xf9, 0x7b, 0xe2,
|
||||
0x58, 0xfc, 0x94, 0x84, 0xeb, 0x5f, 0xbf, 0x07, 0x00, 0x00, 0xff, 0xff, 0x2b, 0xde, 0x2c, 0x5b,
|
||||
0x8f, 0x01, 0x00, 0x00,
|
||||
}
|
25
cmd/minimatch/main.go
Normal file
25
cmd/minimatch/main.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package main is the minimatch in-process testing binary for Open Match.
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app/minimatch"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appmain.RunApplication("minimatch", minimatch.BindService)
|
||||
}
|
@ -1,442 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Note: the example only works with the code within the same release/branch.
|
||||
// This is based on the example from the official k8s golang client repository:
|
||||
// k8s.io/client-go/examples/create-update-delete-deployment/
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/open-match/config"
|
||||
"github.com/GoogleCloudPlatform/open-match/internal/metrics"
|
||||
redisHelpers "github.com/GoogleCloudPlatform/open-match/internal/statestorage/redis"
|
||||
"github.com/tidwall/gjson"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
//"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
// Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters).
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
)
|
||||
|
||||
var (
|
||||
// Logrus structured logging setup
|
||||
mmforcLogFields = log.Fields{
|
||||
"app": "openmatch",
|
||||
"component": "mmforc",
|
||||
"caller": "mmforc/main.go",
|
||||
}
|
||||
mmforcLog = log.WithFields(mmforcLogFields)
|
||||
|
||||
// Viper config management setup
|
||||
cfg = viper.New()
|
||||
err = errors.New("")
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Logrus structured logging initialization
|
||||
// Add a hook to the logger to auto-count log lines for metrics output thru OpenCensus
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
log.AddHook(metrics.NewHook(MmforcLogLines, KeySeverity))
|
||||
|
||||
// Viper config management initialization
|
||||
cfg, err = config.Read()
|
||||
if err != nil {
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
}).Error("Unable to load config file")
|
||||
}
|
||||
|
||||
if cfg.GetBool("debug") == true {
|
||||
log.SetLevel(log.DebugLevel) // debug only, verbose - turn off in production!
|
||||
mmforcLog.Warn("Debug logging configured. Not recommended for production!")
|
||||
}
|
||||
|
||||
// Configure OpenCensus exporter to Prometheus
|
||||
// metrics.ConfigureOpenCensusPrometheusExporter expects that every OpenCensus view you
|
||||
// want to register is in an array, so append any views you want from other
|
||||
// packages to a single array here.
|
||||
ocMmforcViews := DefaultMmforcViews // mmforc OpenCensus views.
|
||||
// Waiting on https://github.com/opencensus-integrations/redigo/pull/1
|
||||
// ocMmforcViews = append(ocMmforcViews, redis.ObservabilityMetricViews...) // redis OpenCensus views.
|
||||
mmforcLog.WithFields(log.Fields{"viewscount": len(ocMmforcViews)}).Info("Loaded OpenCensus views")
|
||||
metrics.ConfigureOpenCensusPrometheusExporter(cfg, ocMmforcViews)
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
pool := redisHelpers.ConnectionPool(cfg)
|
||||
redisConn := pool.Get()
|
||||
defer redisConn.Close()
|
||||
|
||||
// Get k8s credentials so we can starts k8s Jobs
|
||||
mmforcLog.Info("Attempting to acquire k8s credentials")
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mmforcLog.Info("K8s credentials acquired")
|
||||
|
||||
start := time.Now()
|
||||
checkProposals := true
|
||||
defaultMmfImages := []string{cfg.GetString("defaultImages.mmf.name") + ":" + cfg.GetString("defaultImages.mmf.tag")}
|
||||
defaultEvalImage := cfg.GetString("defaultImages.evaluator.name") + ":" + cfg.GetString("defaultImages.evaluator.tag")
|
||||
|
||||
// main loop; kick off matchmaker functions for profiles in the profile
|
||||
// queue and an evaluator when proposals are in the proposals queue
|
||||
for {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
_ = cancel
|
||||
|
||||
// Get profiles and kick off a job for each
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"profileQueueName": cfg.GetString("queues.profiles.name"),
|
||||
"pullCount": cfg.GetInt("queues.profiles.pullCount"),
|
||||
"query": "SPOP",
|
||||
"component": "statestorage",
|
||||
}).Info("Retreiving match profiles")
|
||||
|
||||
results, err := redis.Strings(redisConn.Do("SPOP",
|
||||
cfg.GetString("queues.profiles.name"), cfg.GetInt("queues.profiles.pullCount")))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if len(results) > 0 {
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"numProfiles": len(results),
|
||||
}).Info("Starting MMF jobs...")
|
||||
|
||||
for _, profile := range results {
|
||||
// Kick off the job asynchrnously
|
||||
go mmfunc(ctx, profile, cfg, defaultMmfImages, clientset, pool)
|
||||
// Count the number of jobs running
|
||||
redisHelpers.Increment(context.Background(), pool, "concurrentMMFs")
|
||||
}
|
||||
} else {
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"profileQueueName": cfg.GetString("queues.profiles.name"),
|
||||
}).Warn("Unable to retreive match profiles from statestorage - have you entered any?")
|
||||
}
|
||||
|
||||
// Check to see if we should run the evaluator.
|
||||
// Get number of running MMFs
|
||||
r, err := redisHelpers.Retrieve(context.Background(), pool, "concurrentMMFs")
|
||||
|
||||
if err != nil {
|
||||
mmforcLog.Println(err)
|
||||
if err.Error() == "redigo: nil returned" {
|
||||
// No MMFs have run since we last evaluated; reset timer and loop
|
||||
mmforcLog.Debug("Number of concurrentMMFs is nil")
|
||||
start = time.Now()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
}
|
||||
continue
|
||||
}
|
||||
numRunning, err := strconv.Atoi(r)
|
||||
if err != nil {
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
}).Error("Issue retrieving number of currently running MMFs")
|
||||
}
|
||||
|
||||
// We are ready to evaluate either when all MMFs are complete, or the
|
||||
// timeout is reached.
|
||||
//
|
||||
// Tuning how frequently the evaluator runs is a complex topic and
|
||||
// probably only of interest to users running large-scale production
|
||||
// workloads with many concurrently running matchmaking functions,
|
||||
// which have some overlap in the matchmaking player pools. Suffice to
|
||||
// say that under load, this switch should almost always trigger the
|
||||
// timeout interval code path. The concurrentMMFs check to see how
|
||||
// many are still running is meant as a short-circuit to prevent
|
||||
// waiting to run the evaluator when all your MMFs are already
|
||||
// finished.
|
||||
switch {
|
||||
case time.Since(start).Seconds() >= float64(cfg.GetInt("interval.evaluator")):
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"interval": cfg.GetInt("interval.evaluator"),
|
||||
}).Info("Maximum evaluator interval exceeded")
|
||||
checkProposals = true
|
||||
|
||||
// Opencensus tagging
|
||||
ctx, _ = tag.New(ctx, tag.Insert(KeyEvalReason, "interval_exceeded"))
|
||||
case numRunning <= 0:
|
||||
mmforcLog.Info("All MMFs complete")
|
||||
checkProposals = true
|
||||
numRunning = 0
|
||||
ctx, _ = tag.New(ctx, tag.Insert(KeyEvalReason, "mmfs_completed"))
|
||||
}
|
||||
|
||||
if checkProposals {
|
||||
// Make sure there are proposals in the queue.
|
||||
checkProposals = false
|
||||
mmforcLog.Info("Checking statestorage for match object proposals")
|
||||
results, err := redisHelpers.Count(context.Background(), pool, cfg.GetString("queues.proposals.name"))
|
||||
switch {
|
||||
case err != nil:
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
}).Error("Couldn't retrieve the length of the proposal queue from statestorage!")
|
||||
case results == 0:
|
||||
mmforcLog.WithFields(log.Fields{}).Warn("No proposals in the queue!")
|
||||
default:
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"numProposals": results,
|
||||
}).Info("Proposals available, evaluating!")
|
||||
go evaluator(ctx, cfg, defaultEvalImage, clientset)
|
||||
}
|
||||
_, err = redisHelpers.Delete(context.Background(), pool, "concurrentMMFs")
|
||||
if err != nil {
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
}).Error("Error deleting concurrent MMF counter!")
|
||||
}
|
||||
start = time.Now()
|
||||
}
|
||||
|
||||
// TODO: Make this tunable via config.
|
||||
// A sleep here is not critical but just a useful safety valve in case
|
||||
// things are broken, to keep the main loop from going all-out and spamming the log.
|
||||
mainSleep := 1000
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"ms": mainSleep,
|
||||
}).Info("Sleeping...")
|
||||
time.Sleep(time.Duration(mainSleep) * time.Millisecond)
|
||||
} // End main for loop
|
||||
}
|
||||
|
||||
// mmfunc generates a k8s job that runs the specified mmf container image.
|
||||
func mmfunc(ctx context.Context, profile string, cfg *viper.Viper, imageNames []string, clientset *kubernetes.Clientset, pool *redis.Pool) {
|
||||
// Generate the various keys/names, some of which must be populated to the k8s job.
|
||||
ids := strings.Split(profile, ".")
|
||||
moID := ids[0]
|
||||
proID := ids[1]
|
||||
timestamp := strconv.Itoa(int(time.Now().Unix()))
|
||||
jobName := timestamp + "." + moID + "." + proID
|
||||
|
||||
// Read the full profile from redis and access any keys that are important to deciding how MMFs are run.
|
||||
profile, err := redisHelpers.Retrieve(ctx, pool, proID)
|
||||
if err != nil {
|
||||
// Note that we couldn't read the profile, and try to run the mmf with default settings.
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"jobName": moID,
|
||||
"profile": proID,
|
||||
"containerImages": imageNames,
|
||||
}).Warn("Failure retreiving full profile from statestorage - attempting to run default mmf container")
|
||||
} else {
|
||||
profileImageNames := gjson.Get(profile, cfg.GetString("jsonkeys.mmfImages"))
|
||||
|
||||
// Got profile from state storage, make sure it is valid
|
||||
if gjson.Valid(profile) && profileImageNames.Exists() {
|
||||
switch profileImageNames.Type.String() {
|
||||
case "String":
|
||||
// case: only one image name at this key.
|
||||
imageNames = []string{profileImageNames.String()}
|
||||
case "JSON":
|
||||
// case: Array of image names at this key.
|
||||
// TODO: support multiple MMFs per profile. Doing this will require that
|
||||
// we generate an proposal ID and populate it to the env vars for each
|
||||
// mmf, so they can each write a proposal for the same profile
|
||||
// without stomping each other. (The evaluator would then be
|
||||
// responsible for selecting the proposal to send to the backendapi)
|
||||
imageNames = []string{}
|
||||
|
||||
// Pattern for iterating through a gjson.Result
|
||||
// https://github.com/tidwall/gjson#iterate-through-an-object-or-array
|
||||
profileImageNames.ForEach(func(_, name gjson.Result) bool {
|
||||
// TODO: Swap these two lines when multiple image support is ready
|
||||
// imageNames = append(imageNames, name.String())
|
||||
imageNames = []string{name.String()}
|
||||
return true
|
||||
})
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"jobName": moID,
|
||||
"profile": proID,
|
||||
"containerImages": imageNames,
|
||||
}).Warn("Profile specifies multiple MMF container images (NYI), running only the last image provided")
|
||||
}
|
||||
} else {
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"jobName": moID,
|
||||
"profile": proID,
|
||||
"containerImages": imageNames,
|
||||
}).Warn("Profile JSON was invalid or did not contain a MMF container image name - attempting to run default mmf container")
|
||||
}
|
||||
}
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"jobName": moID,
|
||||
"profile": proID,
|
||||
"containerImage": imageNames,
|
||||
}).Info("Attempting to create mmf k8s job")
|
||||
|
||||
// Create Jobs
|
||||
// TODO: Handle returned errors
|
||||
// TODO: Support multiple MMFs per profile.
|
||||
// NOTE: For now, always send this an array of length 1 specifying the
|
||||
// single MMF container image name you want to run, until multi-mmf
|
||||
// profiles are supported. If you send it more than one, you will get
|
||||
// undefined (but definitely degenerate) behavior!
|
||||
for _, imageName := range imageNames {
|
||||
// Kick off Job with this image name
|
||||
_ = submitJob(imageName, jobName, clientset)
|
||||
if err != nil {
|
||||
// Record failure & log
|
||||
stats.Record(ctx, mmforcMmfFailures.M(1))
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"jobName": moID,
|
||||
"profile": proID,
|
||||
"containerImage": imageName,
|
||||
}).Error("MMF job submission failure!")
|
||||
} else {
|
||||
// Record Success
|
||||
stats.Record(ctx, mmforcMmfs.M(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// evaluator generates a k8s job that runs the specified evaluator container image.
|
||||
func evaluator(ctx context.Context, cfg *viper.Viper, imageName string, clientset *kubernetes.Clientset) {
|
||||
// Generate the job name
|
||||
timestamp := strconv.Itoa(int(time.Now().Unix()))
|
||||
jobName := timestamp + ".evaluator"
|
||||
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"jobName": jobName,
|
||||
"containerImage": imageName,
|
||||
}).Info("Attempting to create evaluator k8s job")
|
||||
|
||||
// Create Job
|
||||
// TODO: Handle returned errors
|
||||
_ = submitJob(imageName, jobName, clientset)
|
||||
if err != nil {
|
||||
// Record failure & log
|
||||
stats.Record(ctx, mmforcEvalFailures.M(1))
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
"jobName": jobName,
|
||||
"containerImage": imageName,
|
||||
}).Error("Evaluator job submission failure!")
|
||||
} else {
|
||||
// Record success
|
||||
stats.Record(ctx, mmforcEvals.M(1))
|
||||
}
|
||||
}
|
||||
|
||||
// submitJob submits a job to kubernetes
|
||||
func submitJob(imageName string, jobName string, clientset *kubernetes.Clientset) error {
|
||||
job := generateJobSpec(jobName, imageName)
|
||||
|
||||
// Get the namespace for the job from the current namespace, otherwise, use default
|
||||
namespace := os.Getenv("METADATA_NAMESPACE")
|
||||
if len(namespace) == 0 {
|
||||
namespace = apiv1.NamespaceDefault
|
||||
}
|
||||
|
||||
// Submit kubernetes job
|
||||
jobsClient := clientset.BatchV1().Jobs(namespace)
|
||||
result, err := jobsClient.Create(job)
|
||||
if err != nil {
|
||||
// TODO: replace queued profiles if things go south
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
}).Error("Couldn't create k8s job!")
|
||||
}
|
||||
|
||||
mmforcLog.WithFields(log.Fields{
|
||||
"jobName": result.GetObjectMeta().GetName(),
|
||||
}).Info("Created job.")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// generateJobSpec is a PoC to test that all the k8s job generation code works.
|
||||
// In the future we should be decoding into the client object using one of the
|
||||
// codecs on an input JSON, or piggyback on job templates.
|
||||
// https://github.com/kubernetes/client-go/issues/193
|
||||
// TODO: many fields in this job spec assume the container image is an mmf, but
|
||||
// we use this to kick the evaluator containers too, should be updated to
|
||||
// reflect that
|
||||
func generateJobSpec(jobName string, imageName string) *batchv1.Job {
|
||||
|
||||
job := &batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: jobName,
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: int32Ptr(1),
|
||||
Template: apiv1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"app": "mmf", // TODO: have this reflect mmf vs evaluator
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
// Unused; here as an example.
|
||||
// Later we can put params here and read them using the
|
||||
// k8s downward API volumes
|
||||
"profile": "exampleprofile",
|
||||
},
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
RestartPolicy: "Never",
|
||||
Containers: []apiv1.Container{
|
||||
{
|
||||
// TODO: have these reflect mmf vs evaluator
|
||||
Name: "mmf",
|
||||
Image: imageName,
|
||||
ImagePullPolicy: "Always",
|
||||
Env: []apiv1.EnvVar{
|
||||
{
|
||||
Name: "PROFILE",
|
||||
Value: jobName,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return job
|
||||
}
|
||||
|
||||
// readability functions used by generateJobSpec
|
||||
func int32Ptr(i int32) *int32 { return &i }
|
||||
func strPtr(i string) *string { return &i }
|
@ -1 +0,0 @@
|
||||
../../config/matchmaker_config.json
|
@ -1,128 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/view"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
// OpenCensus Measures. These are exported as metrics to your monitoring system
|
||||
// https://godoc.org/go.opencensus.io/stats
|
||||
//
|
||||
// When making opencensus stats, the 'name' param, with forward slashes changed
|
||||
// to underscores, is appended to the 'namespace' value passed to the
|
||||
// prometheus exporter to become the Prometheus metric name. You can also look
|
||||
// into having Prometheus rewrite your metric names on scrape.
|
||||
//
|
||||
// For example:
|
||||
// - defining the promethus export namespace "open_match" when instanciating the exporter:
|
||||
// pe, err := promethus.NewExporter(promethus.Options{Namespace: "open_match"})
|
||||
// - and naming the request counter "backend/requests_total":
|
||||
// MGrpcRequests := stats.Int64("backendapi/requests_total", ...
|
||||
// - results in the prometheus metric name:
|
||||
// open_match_backendapi_requests_total
|
||||
// - [note] when using opencensus views to aggregate the metrics into
|
||||
// distribution buckets and such, multiple metrics
|
||||
// will be generated with appended types ("<metric>_bucket",
|
||||
// "<metric>_count", "<metric>_sum", for example)
|
||||
//
|
||||
// In addition, OpenCensus stats propogated to Prometheus have the following
|
||||
// auto-populated labels pulled from kubernetes, which we should avoid to
|
||||
// prevent overloading and having to use the HonorLabels param in Prometheus.
|
||||
//
|
||||
// - Information about the k8s pod being monitored:
|
||||
// "pod" (name of the monitored k8s pod)
|
||||
// "namespace" (k8s namespace of the monitored pod)
|
||||
// - Information about how promethus is gathering the metrics:
|
||||
// "instance" (IP and port number being scraped by prometheus)
|
||||
// "job" (name of the k8s service being scraped by prometheus)
|
||||
// "endpoint" (name of the k8s port in the k8s service being scraped by prometheus)
|
||||
//
|
||||
var (
|
||||
// Logging instrumentation
|
||||
// There's no need to record this measurement directly if you use
|
||||
// the logrus hook provided in metrics/helper.go after instantiating the
|
||||
// logrus instance in your application code.
|
||||
// https://godoc.org/github.com/sirupsen/logrus#LevelHooks
|
||||
MmforcLogLines = stats.Int64("mmforc/logs_total", "Number of Backend API lines logged", "1")
|
||||
|
||||
// Counting operations
|
||||
mmforcMmfs = stats.Int64("mmforc/mmfs_total", "Number of mmf jobs submitted to kubernetes", "1")
|
||||
mmforcMmfFailures = stats.Int64("mmforc/mmf/failures_total", "Number of failures attempting to submit mmf jobs to kubernetes", "1")
|
||||
mmforcEvals = stats.Int64("mmforc/evaluators_total", "Number of evaluator jobs submitted to kubernetes", "1")
|
||||
mmforcEvalFailures = stats.Int64("mmforc/evaluator/failures_total", "Number of failures attempting to submit evaluator jobs to kubernetes", "1")
|
||||
)
|
||||
|
||||
var (
|
||||
// KeyEvalReason is used to tag which code path caused the evaluator to run.
|
||||
KeyEvalReason, _ = tag.NewKey("evalReason")
|
||||
// KeySeverity is used to tag a the severity of a log message.
|
||||
KeySeverity, _ = tag.NewKey("severity")
|
||||
)
|
||||
|
||||
var (
|
||||
// Latency in buckets:
|
||||
// [>=0ms, >=25ms, >=50ms, >=75ms, >=100ms, >=200ms, >=400ms, >=600ms, >=800ms, >=1s, >=2s, >=4s, >=6s]
|
||||
latencyDistribution = view.Distribution(0, 25, 50, 75, 100, 200, 400, 600, 800, 1000, 2000, 4000, 6000)
|
||||
)
|
||||
|
||||
// Package metrics provides some convience views.
|
||||
// You need to register the views for the data to actually be collected.
|
||||
// Note: The OpenCensus View 'Description' is exported to Prometheus as the HELP string.
|
||||
// Note: If you get a "Failed to export to Prometheus: inconsistent label
|
||||
// cardinality" error, chances are you forgot to set the tags specified in the
|
||||
// view for a given measure when you tried to do a stats.Record()
|
||||
var (
|
||||
mmforcMmfsCountView = &view.View{
|
||||
Name: "mmforc/mmfs",
|
||||
Measure: mmforcMmfs,
|
||||
Description: "The number of mmf jobs submitted to kubernetes",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
mmforcMmfFailuresCountView = &view.View{
|
||||
Name: "mmforc/mmf/failures",
|
||||
Measure: mmforcMmfFailures,
|
||||
Description: "The number of mmf jobs that failed submission to kubernetes",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
mmforcEvalsCountView = &view.View{
|
||||
Name: "mmforc/evaluators",
|
||||
Measure: mmforcEvals,
|
||||
Description: "The number of evaluator jobs submitted to kubernetes",
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{KeyEvalReason},
|
||||
}
|
||||
|
||||
mmforcEvalFailuresCountView = &view.View{
|
||||
Name: "mmforc/evaluator/failures",
|
||||
Measure: mmforcEvalFailures,
|
||||
Description: "The number of evaluator jobs that failed submission to kubernetes",
|
||||
Aggregation: view.Count(),
|
||||
TagKeys: []tag.Key{KeyEvalReason},
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultMmforcViews are the default matchmaker orchestrator OpenCensus measure views.
|
||||
var DefaultMmforcViews = []*view.View{
|
||||
mmforcEvalsCountView,
|
||||
mmforcMmfFailuresCountView,
|
||||
mmforcMmfsCountView,
|
||||
mmforcEvalFailuresCountView,
|
||||
}
|
25
cmd/query/query.go
Normal file
25
cmd/query/query.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package main is the query service for Open Match.
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app/query"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appmain.RunApplication("query", query.BindService)
|
||||
}
|
24
cmd/scale-backend/main.go
Normal file
24
cmd/scale-backend/main.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/examples/scale/backend"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appmain.RunApplication("scale", backend.BindService)
|
||||
}
|
22
cmd/scale-evaluator/main.go
Normal file
22
cmd/scale-evaluator/main.go
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
scaleEvaluator "open-match.dev/open-match/examples/scale/evaluator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
scaleEvaluator.Run()
|
||||
}
|
24
cmd/scale-frontend/main.go
Normal file
24
cmd/scale-frontend/main.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/examples/scale/frontend"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appmain.RunApplication("scale", frontend.BindService)
|
||||
}
|
23
cmd/scale-mmf/main.go
Normal file
23
cmd/scale-mmf/main.go
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
scaleMmf "open-match.dev/open-match/examples/scale/mmf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
scaleMmf.Run()
|
||||
}
|
10
cmd/swaggerui/config.json
Normal file
10
cmd/swaggerui/config.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"urls": [
|
||||
{"name": "Frontend", "url": "https://open-match.dev/api/v0.0.0-dev/frontend.swagger.json"},
|
||||
{"name": "Backend", "url": "https://open-match.dev/api/v0.0.0-dev/backend.swagger.json"},
|
||||
{"name": "Query", "url": "https://open-match.dev/api/v0.0.0-dev/query.swagger.json"},
|
||||
{"name": "MatchFunction", "url": "https://open-match.dev/api/v0.0.0-dev/matchfunction.swagger.json"},
|
||||
{"name": "Synchronizer", "url": "https://open-match.dev/api/v0.0.0-dev/synchronizer.swagger.json"},
|
||||
{"name": "Evaluator", "url": "https://open-match.dev/api/v0.0.0-dev/evaluator.swagger.json"}
|
||||
]
|
||||
}
|
24
cmd/swaggerui/swaggerui.go
Normal file
24
cmd/swaggerui/swaggerui.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package main is a simple webserver for hosting Open Match Swagger UI.
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app/swaggerui"
|
||||
)
|
||||
|
||||
func main() {
|
||||
swaggerui.RunApplication()
|
||||
}
|
25
cmd/synchronizer/synchronizer.go
Normal file
25
cmd/synchronizer/synchronizer.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package main is the synchronizer service for Open Match.
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app/synchronizer"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appmain.RunApplication("synchronizer", synchronizer.BindService)
|
||||
}
|
113
config/config.go
113
config/config.go
@ -1,113 +0,0 @@
|
||||
/*
|
||||
Package config contains convenience functions for reading and managing viper configs.
|
||||
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package config
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/view"
|
||||
)
|
||||
|
||||
var (
|
||||
// Logrus structured logging setup
|
||||
logFields = log.Fields{
|
||||
"app": "openmatch",
|
||||
"component": "config",
|
||||
"caller": "config/main.go",
|
||||
}
|
||||
cfgLog = log.WithFields(logFields)
|
||||
|
||||
// Map of the config file keys to environment variable names populated by
|
||||
// k8s into pods. Examples of redis-related env vars as written by k8s
|
||||
// REDIS_SENTINEL_PORT_6379_TCP=tcp://10.55.253.195:6379
|
||||
// REDIS_SENTINEL_PORT=tcp://10.55.253.195:6379
|
||||
// REDIS_SENTINEL_PORT_6379_TCP_ADDR=10.55.253.195
|
||||
// REDIS_SENTINEL_SERVICE_PORT=6379
|
||||
// REDIS_SENTINEL_PORT_6379_TCP_PORT=6379
|
||||
// REDIS_SENTINEL_PORT_6379_TCP_PROTO=tcp
|
||||
// REDIS_SENTINEL_SERVICE_HOST=10.55.253.195
|
||||
envMappings = map[string]string{
|
||||
"redis.hostname": "REDIS_SENTINEL_SERVICE_HOST",
|
||||
"redis.port": "REDIS_SENTINEL_SERVICE_PORT",
|
||||
"redis.pool.maxIdle": "REDIS_POOL_MAXIDLE",
|
||||
"redis.pool.maxActive": "REDIS_POOL_MAXACTIVE",
|
||||
"redis.pool.idleTimeout": "REDIS_POOL_IDLETIMEOUT",
|
||||
"debug": "DEBUG",
|
||||
}
|
||||
|
||||
// Viper config management setup
|
||||
cfg = viper.New()
|
||||
|
||||
// OpenCensus
|
||||
cfgVarCount = stats.Int64("config/vars_total", "Number of config vars read during initialization", "1")
|
||||
// CfgVarCountView is the Open Census view for the cfgVarCount measure.
|
||||
CfgVarCountView = &view.View{
|
||||
Name: "config/vars_total",
|
||||
Measure: cfgVarCount,
|
||||
Description: "The number of config vars read during initialization",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
)
|
||||
|
||||
// Read reads a config file into a viper.Viper instance and associates environment vars defined in
|
||||
// config.envMappings
|
||||
func Read() (*viper.Viper, error) {
|
||||
|
||||
// Viper config management initialization
|
||||
cfg.SetConfigType("json")
|
||||
cfg.SetConfigName("matchmaker_config")
|
||||
cfg.AddConfigPath(".")
|
||||
|
||||
// Read in config file using Viper
|
||||
err := cfg.ReadInConfig()
|
||||
if err != nil {
|
||||
cfgLog.WithFields(log.Fields{
|
||||
"error": err.Error(),
|
||||
}).Fatal("Fatal error reading config file")
|
||||
}
|
||||
|
||||
// Bind this envvars to viper config vars.
|
||||
// https://github.com/spf13/viper#working-with-environment-variables
|
||||
// One important thing to recognize when working with ENV variables is
|
||||
// that the value will be read each time it is accessed. Viper does not
|
||||
// fix the value when the BindEnv is called.
|
||||
for cfgKey, envVar := range envMappings {
|
||||
err = cfg.BindEnv(cfgKey, envVar)
|
||||
|
||||
if err != nil {
|
||||
cfgLog.WithFields(log.Fields{
|
||||
"configkey": cfgKey,
|
||||
"envvar": envVar,
|
||||
"error": err.Error(),
|
||||
"module": "config",
|
||||
}).Warn("Unable to bind environment var as a config variable")
|
||||
|
||||
} else {
|
||||
cfgLog.WithFields(log.Fields{
|
||||
"configkey": cfgKey,
|
||||
"envvar": envVar,
|
||||
"module": "config",
|
||||
}).Info("Binding environment var as a config variable")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return cfg, err
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
{
|
||||
"debug": true,
|
||||
"api": {
|
||||
"backend": {
|
||||
"port": 50505
|
||||
},
|
||||
"frontend": {
|
||||
"port": 50504
|
||||
}
|
||||
},
|
||||
"metrics": {
|
||||
"port": 9555,
|
||||
"endpoint": "/metrics",
|
||||
"reportingPeriod": 5
|
||||
},
|
||||
"queues": {
|
||||
"profiles": {
|
||||
"name": "profileq",
|
||||
"pullCount": 100
|
||||
},
|
||||
"proposals": {
|
||||
"name": "proposalq"
|
||||
}
|
||||
},
|
||||
"defaultImages": {
|
||||
"evaluator": {
|
||||
"name": "gcr.io/matchmaker-dev-201405/openmatch-evaluator",
|
||||
"tag": "dev"
|
||||
},
|
||||
"mmf": {
|
||||
"name": "gcr.io/matchmaker-dev-201405/openmatch-mmf",
|
||||
"tag": "dev"
|
||||
}
|
||||
},
|
||||
"redis": {
|
||||
"user": "",
|
||||
"password": "",
|
||||
"pool" : {
|
||||
"maxIdle" : 3,
|
||||
"maxActive" : 0,
|
||||
"idleTimeout" : 60
|
||||
}
|
||||
},
|
||||
"jsonkeys": {
|
||||
"mmfImages": "imagename",
|
||||
"roster": "profile.roster",
|
||||
"connstring": "connstring"
|
||||
},
|
||||
"interval": {
|
||||
"evaluator": 10,
|
||||
"resultsTimeout": 30
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
{
|
||||
"apiVersion":"extensions/v1beta1",
|
||||
"kind":"Deployment",
|
||||
"metadata":{
|
||||
"name":"om-backendapi",
|
||||
"labels":{
|
||||
"app":"openmatch",
|
||||
"component": "backend"
|
||||
}
|
||||
},
|
||||
"spec":{
|
||||
"replicas":1,
|
||||
"selector":{
|
||||
"matchLabels":{
|
||||
"app":"openmatch",
|
||||
"component": "backend"
|
||||
}
|
||||
},
|
||||
"template":{
|
||||
"metadata":{
|
||||
"labels":{
|
||||
"app":"openmatch",
|
||||
"component": "backend"
|
||||
}
|
||||
},
|
||||
"spec":{
|
||||
"containers":[
|
||||
{
|
||||
"name":"om-backend",
|
||||
"image":"gcr.io/matchmaker-dev-201405/openmatch-backendapi:dev",
|
||||
"imagePullPolicy":"Always",
|
||||
"ports": [
|
||||
{
|
||||
"name": "grpc",
|
||||
"containerPort": 50505
|
||||
},
|
||||
{
|
||||
"name": "metrics",
|
||||
"containerPort": 9555
|
||||
}
|
||||
],
|
||||
"resources":{
|
||||
"requests":{
|
||||
"memory":"100Mi",
|
||||
"cpu":"100m"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "om-backendapi"
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"app": "openmatch",
|
||||
"component": "backend"
|
||||
},
|
||||
"ports": [
|
||||
{
|
||||
"protocol": "TCP",
|
||||
"port": 50505,
|
||||
"targetPort": "grpc"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
{
|
||||
"apiVersion":"extensions/v1beta1",
|
||||
"kind":"Deployment",
|
||||
"metadata":{
|
||||
"name":"om-frontendapi",
|
||||
"labels":{
|
||||
"app":"openmatch",
|
||||
"component": "frontend"
|
||||
}
|
||||
},
|
||||
"spec":{
|
||||
"replicas":1,
|
||||
"selector":{
|
||||
"matchLabels":{
|
||||
"app":"openmatch",
|
||||
"component": "frontend"
|
||||
}
|
||||
},
|
||||
"template":{
|
||||
"metadata":{
|
||||
"labels":{
|
||||
"app":"openmatch",
|
||||
"component": "frontend"
|
||||
}
|
||||
},
|
||||
"spec":{
|
||||
"containers":[
|
||||
{
|
||||
"name":"om-frontendapi",
|
||||
"image":"gcr.io/matchmaker-dev-201405/openmatch-frontendapi:dev",
|
||||
"imagePullPolicy":"Always",
|
||||
"ports": [
|
||||
{
|
||||
"name": "grpc",
|
||||
"containerPort": 50504
|
||||
},
|
||||
{
|
||||
"name": "metrics",
|
||||
"containerPort": 9555
|
||||
}
|
||||
],
|
||||
"resources":{
|
||||
"requests":{
|
||||
"memory":"100Mi",
|
||||
"cpu":"100m"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "om-frontendapi"
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"app": "openmatch",
|
||||
"component": "frontend"
|
||||
},
|
||||
"ports": [
|
||||
{
|
||||
"protocol": "TCP",
|
||||
"port": 50504,
|
||||
"targetPort": "grpc"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"apiVersion": "monitoring.coreos.com/v1",
|
||||
"kind": "ServiceMonitor",
|
||||
"metadata": {
|
||||
"name": "openmatch-metrics",
|
||||
"labels": {
|
||||
"app": "openmatch",
|
||||
"agent": "opencensus",
|
||||
"destination": "prometheus"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "openmatch",
|
||||
"agent": "opencensus",
|
||||
"destination": "prometheus"
|
||||
}
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"port": "metrics",
|
||||
"interval": "10s"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "om-frontend-metrics",
|
||||
"labels": {
|
||||
"app": "openmatch",
|
||||
"component": "frontend",
|
||||
"agent": "opencensus",
|
||||
"destination": "prometheus"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"app": "openmatch",
|
||||
"component": "frontend"
|
||||
},
|
||||
"ports": [
|
||||
{
|
||||
"name": "metrics",
|
||||
"targetPort": 9555,
|
||||
"port": 19555
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "om-backend-metrics",
|
||||
"labels": {
|
||||
"app": "openmatch",
|
||||
"component": "backend",
|
||||
"agent": "opencensus",
|
||||
"destination": "prometheus"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"app": "openmatch",
|
||||
"component": "backend"
|
||||
},
|
||||
"ports": [
|
||||
{
|
||||
"name": "metrics",
|
||||
"targetPort": 9555,
|
||||
"port": 29555
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "om-mmforc-metrics",
|
||||
"labels": {
|
||||
"app": "openmatch",
|
||||
"component": "mmforc",
|
||||
"agent": "opencensus",
|
||||
"destination": "prometheus"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"app": "openmatch",
|
||||
"component": "mmforc"
|
||||
},
|
||||
"ports": [
|
||||
{
|
||||
"name": "metrics",
|
||||
"targetPort": 9555,
|
||||
"port": 39555
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
{
|
||||
"apiVersion":"extensions/v1beta1",
|
||||
"kind":"Deployment",
|
||||
"metadata":{
|
||||
"name":"om-mmforc",
|
||||
"labels":{
|
||||
"app":"openmatch",
|
||||
"component": "mmforc"
|
||||
}
|
||||
},
|
||||
"spec":{
|
||||
"replicas":1,
|
||||
"selector":{
|
||||
"matchLabels":{
|
||||
"app":"openmatch",
|
||||
"component": "mmforc"
|
||||
}
|
||||
},
|
||||
"template":{
|
||||
"metadata":{
|
||||
"labels":{
|
||||
"app":"openmatch",
|
||||
"component": "mmforc"
|
||||
}
|
||||
},
|
||||
"spec":{
|
||||
"containers":[
|
||||
{
|
||||
"name":"om-mmforc",
|
||||
"image":"gcr.io/matchmaker-dev-201405/openmatch-mmforc:dev",
|
||||
"imagePullPolicy":"Always",
|
||||
"ports": [
|
||||
{
|
||||
"name": "metrics",
|
||||
"containerPort":9555
|
||||
}
|
||||
],
|
||||
"resources":{
|
||||
"requests":{
|
||||
"memory":"100Mi",
|
||||
"cpu":"100m"
|
||||
}
|
||||
},
|
||||
"env":[
|
||||
{
|
||||
"name":"METADATA_NAMESPACE",
|
||||
"valueFrom": {
|
||||
"fieldRef": {
|
||||
"fieldPath": "metadata.namespace"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1beta1",
|
||||
"kind": "ClusterRoleBinding",
|
||||
"metadata": {
|
||||
"name": "mmf-sa"
|
||||
},
|
||||
"subjects": [
|
||||
{
|
||||
"kind": "ServiceAccount",
|
||||
"name": "default",
|
||||
"namespace": "default"
|
||||
}
|
||||
],
|
||||
"roleRef": {
|
||||
"kind": "ClusterRole",
|
||||
"name": "cluster-admin",
|
||||
"apiGroup": "rbac.authorization.k8s.io"
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"apiVersion": "monitoring.coreos.com/v1",
|
||||
"kind": "Prometheus",
|
||||
"metadata": {
|
||||
"name": "prometheus"
|
||||
},
|
||||
"spec": {
|
||||
"serviceMonitorSelector": {
|
||||
"matchLabels": {
|
||||
"app": "openmatch"
|
||||
}
|
||||
},
|
||||
"serviceAccountName": "prometheus",
|
||||
"resources": {
|
||||
"requests": {
|
||||
"memory": "400Mi"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,266 +0,0 @@
|
||||
{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1beta1",
|
||||
"kind": "ClusterRoleBinding",
|
||||
"metadata": {
|
||||
"name": "prometheus-operator"
|
||||
},
|
||||
"roleRef": {
|
||||
"apiGroup": "rbac.authorization.k8s.io",
|
||||
"kind": "ClusterRole",
|
||||
"name": "prometheus-operator"
|
||||
},
|
||||
"subjects": [
|
||||
{
|
||||
"kind": "ServiceAccount",
|
||||
"name": "prometheus-operator",
|
||||
"namespace": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ServiceAccount",
|
||||
"metadata": {
|
||||
"name": "prometheus"
|
||||
}
|
||||
}
|
||||
{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1beta1",
|
||||
"kind": "ClusterRole",
|
||||
"metadata": {
|
||||
"name": "prometheus"
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"apiGroups": [
|
||||
""
|
||||
],
|
||||
"resources": [
|
||||
"nodes",
|
||||
"services",
|
||||
"endpoints",
|
||||
"pods"
|
||||
],
|
||||
"verbs": [
|
||||
"get",
|
||||
"list",
|
||||
"watch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"apiGroups": [
|
||||
""
|
||||
],
|
||||
"resources": [
|
||||
"configmaps"
|
||||
],
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
},
|
||||
{
|
||||
"nonResourceURLs": [
|
||||
"/metrics"
|
||||
],
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1beta1",
|
||||
"kind": "ClusterRoleBinding",
|
||||
"metadata": {
|
||||
"name": "prometheus"
|
||||
},
|
||||
"roleRef": {
|
||||
"apiGroup": "rbac.authorization.k8s.io",
|
||||
"kind": "ClusterRole",
|
||||
"name": "prometheus"
|
||||
},
|
||||
"subjects": [
|
||||
{
|
||||
"kind": "ServiceAccount",
|
||||
"name": "prometheus",
|
||||
"namespace": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1beta1",
|
||||
"kind": "ClusterRole",
|
||||
"metadata": {
|
||||
"name": "prometheus-operator"
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"apiGroups": [
|
||||
"extensions"
|
||||
],
|
||||
"resources": [
|
||||
"thirdpartyresources"
|
||||
],
|
||||
"verbs": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"apiGroups": [
|
||||
"apiextensions.k8s.io"
|
||||
],
|
||||
"resources": [
|
||||
"customresourcedefinitions"
|
||||
],
|
||||
"verbs": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"apiGroups": [
|
||||
"monitoring.coreos.com"
|
||||
],
|
||||
"resources": [
|
||||
"alertmanagers",
|
||||
"prometheuses",
|
||||
"prometheuses/finalizers",
|
||||
"servicemonitors"
|
||||
],
|
||||
"verbs": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"apiGroups": [
|
||||
"apps"
|
||||
],
|
||||
"resources": [
|
||||
"statefulsets"
|
||||
],
|
||||
"verbs": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"apiGroups": [
|
||||
""
|
||||
],
|
||||
"resources": [
|
||||
"configmaps",
|
||||
"secrets"
|
||||
],
|
||||
"verbs": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"apiGroups": [
|
||||
""
|
||||
],
|
||||
"resources": [
|
||||
"pods"
|
||||
],
|
||||
"verbs": [
|
||||
"list",
|
||||
"delete"
|
||||
]
|
||||
},
|
||||
{
|
||||
"apiGroups": [
|
||||
""
|
||||
],
|
||||
"resources": [
|
||||
"services",
|
||||
"endpoints"
|
||||
],
|
||||
"verbs": [
|
||||
"get",
|
||||
"create",
|
||||
"update"
|
||||
]
|
||||
},
|
||||
{
|
||||
"apiGroups": [
|
||||
""
|
||||
],
|
||||
"resources": [
|
||||
"nodes"
|
||||
],
|
||||
"verbs": [
|
||||
"list",
|
||||
"watch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"apiGroups": [
|
||||
""
|
||||
],
|
||||
"resources": [
|
||||
"namespaces"
|
||||
],
|
||||
"verbs": [
|
||||
"list"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ServiceAccount",
|
||||
"metadata": {
|
||||
"name": "prometheus-operator"
|
||||
}
|
||||
}
|
||||
{
|
||||
"apiVersion": "extensions/v1beta1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"k8s-app": "prometheus-operator"
|
||||
},
|
||||
"name": "prometheus-operator"
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"k8s-app": "prometheus-operator"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"args": [
|
||||
"--kubelet-service=kube-system/kubelet",
|
||||
"--config-reloader-image=quay.io/coreos/configmap-reload:v0.0.1"
|
||||
],
|
||||
"image": "quay.io/coreos/prometheus-operator:v0.17.0",
|
||||
"name": "prometheus-operator",
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 8080,
|
||||
"name": "http"
|
||||
}
|
||||
],
|
||||
"resources": {
|
||||
"limits": {
|
||||
"cpu": "200m",
|
||||
"memory": "100Mi"
|
||||
},
|
||||
"requests": {
|
||||
"cpu": "100m",
|
||||
"memory": "50Mi"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"securityContext": {
|
||||
"runAsNonRoot": true,
|
||||
"runAsUser": 65534
|
||||
},
|
||||
"serviceAccountName": "prometheus-operator"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
"metadata": {
|
||||
"name": "prometheus"
|
||||
},
|
||||
"spec": {
|
||||
"type": "NodePort",
|
||||
"ports": [
|
||||
{
|
||||
"name": "web",
|
||||
"nodePort": 30900,
|
||||
"port": 9090,
|
||||
"protocol": "TCP",
|
||||
"targetPort": "web"
|
||||
}
|
||||
],
|
||||
"selector": {
|
||||
"prometheus": "prometheus"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
{
|
||||
"apiVersion": "extensions/v1beta1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "redis-master"
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "mm",
|
||||
"tier": "storage"
|
||||
}
|
||||
},
|
||||
"replicas": 1,
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "mm",
|
||||
"tier": "storage"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "redis-master",
|
||||
"image": "redis:4.0.11",
|
||||
"ports": [
|
||||
{
|
||||
"name": "redis",
|
||||
"containerPort": 6379
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"kind": "Service",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "redis-sentinel"
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"app": "mm",
|
||||
"tier": "storage"
|
||||
},
|
||||
"ports": [
|
||||
{
|
||||
"protocol": "TCP",
|
||||
"port": 6379,
|
||||
"targetPort": "redis"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
16
doc.go
Normal file
16
doc.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package openmatch provides flexible, extensible, and scalable video game matchmaking.
|
||||
package openmatch // import "open-match.dev/open-match"
|
@ -1,109 +1,162 @@
|
||||
# Compiling from source
|
||||
# Development Guide
|
||||
|
||||
All components of Open Match produce (Linux) Docker container images as artifacts, and there are included `Dockerfile`s for each. [Google Cloud Platform Cloud Build](https://cloud.google.com/cloud-build/docs/) users will also find `cloudbuild_<name>.yaml` files for each component in the repository root.
|
||||
Open Match is a collection of [Go](https://golang.org/) gRPC services that run
|
||||
within [Kubernetes](https://kubernetes.io).
|
||||
|
||||
Note: Although Google Cloud Platform includes some free usage, you may incur charges following this guide if you use GCP products.
|
||||
## Install Prerequisites
|
||||
|
||||
**This project has not completed a first-line security audit, and there are definitely going to be some service accounts that are too permissive. This should be fine for testing/development in a local environment, but absolutely should not be used as-is in a production environment.**
|
||||
To build Open Match you'll need the following applications installed.
|
||||
|
||||
## Example of building using Google Cloud Builder
|
||||
* [Git](https://git-scm.com/downloads)
|
||||
* [Go](https://golang.org/doc/install)
|
||||
* Make (Mac: install [XCode](https://itunes.apple.com/us/app/xcode/id497799835))
|
||||
* [Docker](https://docs.docker.com/install/) including the
|
||||
[post-install steps](https://docs.docker.com/install/linux/linux-postinstall/).
|
||||
|
||||
The [Quickstart for Docker](https://cloud.google.com/cloud-build/docs/quickstart-docker) guide explains how to set up a project, enable billing, enable Cloud Build, and install the Cloud SDK if you haven't do these things before. Once you get to 'Preparing source files' you are ready to continue with the steps below.
|
||||
Optional Software
|
||||
|
||||
* Clone this repo to a local machine or Google Cloud Shell session, and cd into it.
|
||||
* Run the following one-line bash script to compile all the images for the first time, and push them to your gcr.io registry. You must enable the [Container Registry API](https://console.cloud.google.com/flows/enableapi?apiid=containerregistry.googleapis.com) first.
|
||||
```
|
||||
for dfile in $(ls Dockerfile.*); do gcloud builds submit --config cloudbuild_${dfile##*.}.yaml; done
|
||||
* [Visual Studio Code](https://code.visualstudio.com/Download) for IDE.
|
||||
Vim and Emacs work to.
|
||||
* [VirtualBox](https://www.virtualbox.org/wiki/Downloads) recommended for
|
||||
[Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/).
|
||||
|
||||
On Debian-based Linux you can install all the required packages (except Go) by
|
||||
running:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y -q make google-cloud-sdk git unzip tar
|
||||
```
|
||||
|
||||
## Example of starting a GKE cluster
|
||||
*It's recommended that you install Go using their instructions because package
|
||||
managers tend to lag behind the latest Go releases.*
|
||||
|
||||
A cluster with mostly default settings will work for this development guide. In the Cloud SDK command below we start it with machines that have 4 vCPUs. Alternatively, you can use the 'Create Cluster' button in [Google Cloud Console]("https://console.cloud.google.com/kubernetes").
|
||||
## Get the Code
|
||||
|
||||
```
|
||||
gcloud container clusters create --machine-type n1-standard-4 open-match-dev-cluster --zone <ZONE>
|
||||
```bash
|
||||
# Create a directory for the project.
|
||||
mkdir -p $HOME/workspace
|
||||
cd $HOME/workspace
|
||||
# Download the source code.
|
||||
git clone https://github.com/googleforgames/open-match.git
|
||||
cd open-match
|
||||
# Print the help for the Makefile commands.
|
||||
make
|
||||
```
|
||||
|
||||
If you don't know which zone to launch the cluster in (`<ZONE>`), you can list all available zones by running the following command.
|
||||
*Typically for contributing you'll want to
|
||||
[create a fork](https://help.github.com/en/articles/fork-a-repo) and use that
|
||||
but for purpose of this guide we'll be using the upstream/master.*
|
||||
|
||||
```
|
||||
gcloud compute zones list
|
||||
## Building code and images
|
||||
|
||||
```bash
|
||||
# Reset workspace
|
||||
make clean
|
||||
# Run tests
|
||||
make test
|
||||
# Build all the images.
|
||||
make build-images -j$(nproc)
|
||||
# Push images to gcr.io (requires Google Cloud SDK installed)
|
||||
make push-images -j$(nproc)
|
||||
# Push images to Docker Hub
|
||||
make REGISTRY=mydockerusername push-images -j$(nproc)
|
||||
# Generate Kubernetes installation YAML files (Note that the trailing '/' is needed here)
|
||||
make install/yaml/
|
||||
```
|
||||
|
||||
## Configuration
|
||||
_**-j$(nproc)** is a flag to tell make to parallelize the commands based on
|
||||
the number of CPUs on your machine._
|
||||
|
||||
Currently, each component reads a local config file `matchmaker_config.json` , and all components assume they have the same configuration. To this end, there is a single centralized config file located in the `<REPO_ROOT>/config/` which is symlinked to each component's subdirectory for convenience when building locally.
|
||||
## Deploying to Kubernetes
|
||||
|
||||
**NOTE** 'defaultImages' container images names in the config file will need to be updated with **your container registry URI**. Here's an example command in Linux to do this (just replace YOUR_REGISTRY_URI with the appropriate location in your environment, should be run from the config directory):
|
||||
```
|
||||
sed -i 's|gcr.io/matchmaker-dev-201405|YOUR_REGISTRY_URI|g' matchmaker_config.json
|
||||
```
|
||||
For MacOS the `-i` flag creates backup files when changing the original file in place. You can use the following command, and then delete the `*.backup` files afterwards if you don't need them anymore:
|
||||
```
|
||||
sed -i'.backup' -e 's|gcr.io/matchmaker-dev-201405|YOUR_REGISTRY_URI|g' matchmaker_config.json
|
||||
```
|
||||
If you are using the gcr.io registry on GCP, the default URI is `gcr.io/<PROJECT_NAME>`.
|
||||
Kubernetes comes in many flavors and Open Match can be used in any of them.
|
||||
|
||||
We plan to replace this with a Kubernetes-managed config with dynamic reloading when development time allows. Pull requests are welcome!
|
||||
_We support GKE ([setup guide](gcloud.md)), Minikube, and Kubernetes in Docker (KinD) in the Makefile.
|
||||
As long as kubectl is configured to talk to your Kubernetes cluster as the
|
||||
default context the Makefile will honor that._
|
||||
|
||||
## Running Open Match in a development environment
|
||||
```bash
|
||||
# Step 1: Create a Kubernetes (k8s) cluster
|
||||
# KinD cluster: make create-kind-cluster/delete-kind-cluster
|
||||
# GKE cluster: make create-gke-cluster/delete-gke-cluster
|
||||
# or create a local Minikube cluster
|
||||
make create-gke-cluster
|
||||
# Step 2: Build and Push Open Match Images to gcr.io
|
||||
make push-images -j$(nproc)
|
||||
# Step 3: Install Open Match in the cluster.
|
||||
make install-chart
|
||||
|
||||
The rest of this guide assumes you have a cluster (example is using GKE, but works on any cluster with a little tweaking), and kubectl configured to administer that cluster, and you've built all the Docker container images described by `Dockerfiles` in the repository root directory and given them the docker tag 'dev'. It assumes you are in the `<REPO_ROOT>/deployments/k8s/` directory.
|
||||
# Create a proxy to Open Match pods so that you can access them locally.
|
||||
# This command consumes a terminal window that you can kill via Ctrl+C.
|
||||
# You can run `curl -X POST http://localhost:51504/v1/frontend/tickets` to send
|
||||
# a DeleteTicket request to the frontend service in the cluster.
|
||||
# Then try visiting http://localhost:3000/ and view the graphs.
|
||||
make proxy
|
||||
|
||||
**NOTE** Kubernetes resources that use container images will need to be updated with **your container registry URI**. Here's an example command in Linux to do this (just replace YOUR_REGISTRY_URI with the appropriate location in your environment):
|
||||
# Teardown the install
|
||||
make delete-chart
|
||||
```
|
||||
sed -i 's|gcr.io/matchmaker-dev-201405|YOUR_REGISTRY_URI|g' *deployment.json
|
||||
```
|
||||
For MacOS the `-i` flag creates backup files when changing the original file in place. You can use the following command, and then delete the `*.backup` files afterwards if you don't need them anymore:
|
||||
```
|
||||
sed -i'.backup' -e 's|gcr.io/matchmaker-dev-201405|YOUR_REGISTRY_URI|g' *deployment.json
|
||||
```
|
||||
If you are using the gcr.io registry on GCP, the default URI is `gcr.io/<PROJECT_NAME>`.
|
||||
|
||||
* Start a copy of redis and a service in front of it:
|
||||
```
|
||||
kubectl apply -f redis_deployment.json
|
||||
kubectl apply -f redis_service.json
|
||||
```
|
||||
* Run the **core components**: the frontend API, the backend API, and the matchmaker function orchestrator (MMFOrc).
|
||||
**NOTE** In order to kick off jobs, the matchmaker function orchestrator needs a service account with permission to administer the cluster. This should be updated to have min required perms before launch, this is pretty permissive but acceptable for closed testing:
|
||||
```
|
||||
kubectl apply -f backendapi_deployment.json
|
||||
kubectl apply -f backendapi_service.json
|
||||
kubectl apply -f frontendapi_deployment.json
|
||||
kubectl apply -f frontendapi_service.json
|
||||
kubectl apply -f mmforc_deployment.json
|
||||
kubectl apply -f mmforc_serviceaccount.json
|
||||
```
|
||||
* [optional, but recommended] Configure the OpenCensus metrics services:
|
||||
```
|
||||
kubectl apply -f metrics_services.json
|
||||
```
|
||||
* [optional] Trying to apply the Kubernetes Prometheus Operator resource definition files without a cluster-admin rolebinding on GKE doesn't work without running the following command first. See https://github.com/coreos/prometheus-operator/issues/357
|
||||
```
|
||||
kubectl create clusterrolebinding projectowner-cluster-admin-binding --clusterrole=cluster-admin --user=<GCP_ACCOUNT>
|
||||
```
|
||||
* [optional, uses beta software] If using Prometheus as your metrics gathering backend, configure the [Prometheus Kubernetes Operator](https://github.com/coreos/prometheus-operator):
|
||||
## Iterating
|
||||
While iterating on the project, you may need to:
|
||||
1. Install/Run everything
|
||||
2. Make some code changes
|
||||
3. Make sure the changes compile by running `make test`
|
||||
4. Build and push Docker images to your personal registry by running `make push-images -j$(nproc)`
|
||||
5. Deploy the code change by running `make install-chart`
|
||||
6. Verify it's working by [looking at the logs](#accessing-logs) or looking at the monitoring dashboard by running `make proxy-grafana`
|
||||
7. Tear down Open Match by running `make delete-chart`
|
||||
|
||||
## Accessing logs
|
||||
To look at Open Match core services' logs, run:
|
||||
```bash
|
||||
# Replace open-match-frontend with the service name that you would like to access
|
||||
kubectl logs -n open-match svc/open-match-frontend
|
||||
```
|
||||
kubectl apply -f prometheus_operator.json
|
||||
kubectl apply -f prometheus.json
|
||||
kubectl apply -f prometheus_service.json
|
||||
kubectl apply -f metrics_servicemonitor.json
|
||||
|
||||
## API References
|
||||
While integrating with Open Match you may want to understand its API surface concepts or interact with it and get a feel for how it works.
|
||||
|
||||
The APIs are defined in `proto` format under the `api/` folder, with references available at [open-match.dev](https://open-match.dev/site/docs/reference/api/).
|
||||
|
||||
You can also run `make proxy-ui` to exposes the Swagger UI for Open Match locally on your computer after [deploying it to Kubernetes](#deploying-to-kubernetes), then go to http://localhost:51500 and view the REST APIs as well as interactively call Open Match.
|
||||
|
||||
By default you will be talking to the frontend server but you can change the target API url to any of the following:
|
||||
|
||||
* api/frontend.swagger.json
|
||||
* api/backend.swagger.json
|
||||
* api/synchronizer.swagger.json
|
||||
* api/query.swagger.json
|
||||
|
||||
For a more current list refer to the api/ directory of this repository. Also matchfunction.swagger.json is not supported.
|
||||
|
||||
## IDE Support
|
||||
|
||||
Open Match is a standard Go project so any IDE that understands that should
|
||||
work. We use [Go Modules](https://github.com/golang/go/wiki/Modules) which is a
|
||||
relatively new feature in Go so make sure the IDE you are using was built around
|
||||
Summer 2019. The latest version of
|
||||
[Visual Studio Code](https://code.visualstudio.com/download) supports it.
|
||||
|
||||
If your IDE is too old you can create a
|
||||
[Go workspace](https://golang.org/doc/code.html#Workspaces).
|
||||
|
||||
```bash
|
||||
# Create the Go workspace in $HOME/workspace/ directory.
|
||||
mkdir -p $HOME/workspace/src/open-match.dev/
|
||||
cd $HOME/workspace/src/open-match.dev/
|
||||
# Download the source code.
|
||||
git clone https://github.com/googleforgames/open-match.git
|
||||
cd open-match
|
||||
export GOPATH=$HOME/workspace/
|
||||
```
|
||||
You should now be able to see the core component pods running using a `kubectl get pods`, and the core component metrics in the Prometheus Web UI by running `kubectl proxy <PROMETHEUS_POD_NAME> 9090:9090` in your local shell, then opening http://localhost:9090/targets in your browser to see which services Prometheus is collecting from.
|
||||
|
||||
### End-to-End testing
|
||||
## Pull Requests
|
||||
|
||||
**Note** The programs provided below are just bare-bones manual testing programs with no automation and no claim of code coverage. This sparseness of this part of the documentation is because we expect to discard all of these tools and write a fully automated end-to-end test suite and a collection of load testing tools, with extensive stats output and tracing capabilities before 1.0 release. Tracing has to be integrated first, which will be in an upcoming release.
|
||||
|
||||
In the end: *caveat emptor*. These tools all work and are quite small, and as such are fairly easy for developers to understand by looking at the code and logging output. They are provided as-is just as a reference point of how to begin experimenting with Open Match integrations.
|
||||
|
||||
* `examples/frontendclient` is a fake client for the Frontend API. It pretends to be a real game client connecting to Open Match and requests a game, then dumps out the connection string it receives. Note that it doesn't actually test the return path by looking for arbitrary results from your matchmaking function; it pauses and tells you the name of a key to set a connection string in directly using a redis-cli client.
|
||||
* `examples/backendclient` is a fake client for the Backend API. It pretends to be a dedicated game server backend connecting to openmatch and sending in a match profile to fill. Once it receives a match object with a roster, it will also issue a call to assign the player IDs, and gives an example connection string. If it never seems to get a match, make sure you're adding players to the pool using the other two tools.
|
||||
* `test/cmd/client` is a (VERY) basic client load simulation tool. It does **not** test the Frontend API - in fact, it ignores it and writes players directly to state storage on its own. It doesn't do anything but loop endlessly, writing players into state storage so you can test your backend integration, and run your custom MMFs and Evaluators (which are only triggered when there are players in the pool).
|
||||
|
||||
### Resources
|
||||
|
||||
* [Prometheus Operator spec](https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md)
|
||||
If you want to submit a Pull Request, `make presubmit` can catch most of the issues your change can run into.
|
||||
|
||||
Our [continuous integration](https://console.cloud.google.com/cloud-build/builds?project=open-match-build)
|
||||
runs against all PRs. In order to see your build results you'll need to
|
||||
become a member of
|
||||
[open-match-discuss@googlegroups.com](https://groups.google.com/forum/#!forum/open-match-discuss).
|
||||
|
@ -1 +0,0 @@
|
||||
*"I notice that all the APIs use gRPC. What if I want to make my calls using REST, or via a Websocket?"** (gateway/proxy OSS projects are available)
|
61
docs/governance/templates/release.md
Normal file
61
docs/governance/templates/release.md
Normal file
@ -0,0 +1,61 @@
|
||||
# v{version}
|
||||
|
||||
This is the {version} release of Open Match.
|
||||
|
||||
Check the [official website](https://open-match.dev) for details on features, installation and usage.
|
||||
|
||||
Release Notes
|
||||
-------------
|
||||
|
||||
**Feature Highlights**
|
||||
{ highlight here the most notable changes and themes at a high level}
|
||||
|
||||
**Breaking Changes**
|
||||
{ detail any behaviors or API surfaces which worked in a previous version which will no longer work correctly }
|
||||
|
||||
> Future releases towards 1.0.0 may still have breaking changes.
|
||||
|
||||
**Security Fixes**
|
||||
{ list any changes which fix vulnerabilities in open match }
|
||||
|
||||
**Enhancements**
|
||||
{ go into details on improvements and changes }
|
||||
|
||||
Usage Requirements
|
||||
-------------
|
||||
* Tested against Kubernetes Version { a list of k8s versions}
|
||||
* Golang Version = v{ required golang version }
|
||||
|
||||
Images
|
||||
------
|
||||
|
||||
```bash
|
||||
# Servers
|
||||
docker pull gcr.io/open-match-public-images/openmatch-backend:{version}
|
||||
docker pull gcr.io/open-match-public-images/openmatch-frontend:{version}
|
||||
docker pull gcr.io/open-match-public-images/openmatch-query:{version}
|
||||
docker pull gcr.io/open-match-public-images/openmatch-synchronizer:{version}
|
||||
|
||||
# Evaluators
|
||||
docker pull gcr.io/open-match-public-images/openmatch-evaluator-go-simple:{version}
|
||||
|
||||
# Sample Match Making Functions
|
||||
docker pull gcr.io/open-match-public-images/openmatch-mmf-go-soloduel:{version}
|
||||
docker pull gcr.io/open-match-public-images/openmatch-mmf-go-pool:{version}
|
||||
|
||||
# Test Clients
|
||||
docker pull gcr.io/open-match-public-images/openmatch-demo-first-match:{version}
|
||||
```
|
||||
|
||||
_This software is currently alpha, and subject to change. Not to be used in production systems._
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
* Follow [Open Match Installation Guide](https://open-match.dev/site/docs/installation/) to setup Open Match in your cluster.
|
||||
|
||||
API Definitions
|
||||
------------
|
||||
|
||||
- gRPC API Definitions are available in [API references](https://open-match.dev/site/docs/reference/api/) - _Preferred_
|
||||
- HTTP API Definitions are available in [SwaggerUI](https://open-match.dev/site/swaggerui/index.html)
|
24
docs/governance/templates/release.sh
Executable file
24
docs/governance/templates/release.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
# Usage:
|
||||
# ./release.sh 0.5.0-82d034f unstable
|
||||
# ./release.sh [SOURCE VERSION] [DEST VERSION]
|
||||
|
||||
# This is a basic shell script to publish the latest Open Match Images
|
||||
# There's no guardrails yet so use with care.
|
||||
# Purge Images
|
||||
# docker rmi $(docker images -a -q)
|
||||
# 0.4.0-82d034f
|
||||
SOURCE_VERSION=$1
|
||||
DEST_VERSION=$2
|
||||
SOURCE_PROJECT_ID=open-match-build
|
||||
DEST_PROJECT_ID=open-match-public-images
|
||||
IMAGE_NAMES=$(make list-images)
|
||||
|
||||
for name in $IMAGE_NAMES
|
||||
do
|
||||
source_image=gcr.io/$SOURCE_PROJECT_ID/openmatch-$name:$SOURCE_VERSION
|
||||
dest_image=gcr.io/$DEST_PROJECT_ID/openmatch-$name:$DEST_VERSION
|
||||
docker pull $source_image
|
||||
docker tag $source_image $dest_image
|
||||
docker push $dest_image
|
||||
done
|
7
docs/hugo_apiheader.txt
Normal file
7
docs/hugo_apiheader.txt
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: "Open Match API References"
|
||||
linkTitle: "Open Match API References"
|
||||
weight: 2
|
||||
description:
|
||||
This document provides API references for Open Match services.
|
||||
---
|
@ -1 +0,0 @@
|
||||
During alpha, please do not use Open Match as-is in production. To develop against it, please see the [development guide](development.md).
|
@ -1,7 +0,0 @@
|
||||
FROM golang:1.10.3 as builder
|
||||
WORKDIR /go/src/github.com/GoogleCloudPlatform/open-match/examples/backendclient
|
||||
COPY ./ ./
|
||||
RUN go get -d -v
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o backendclient .
|
||||
|
||||
CMD ["./backendclient"]
|
@ -1,8 +0,0 @@
|
||||
steps:
|
||||
- name: 'gcr.io/cloud-builders/docker'
|
||||
args: [
|
||||
'build',
|
||||
'--tag=gcr.io/$PROJECT_ID/openmatch-backendclient:dev',
|
||||
'.'
|
||||
]
|
||||
images: ['gcr.io/$PROJECT_ID/openmatch-backendclient:dev']
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
Stubbed backend api client. This should be run within a k8s cluster, and
|
||||
assumes that the backend api is up and can be accessed through a k8s service
|
||||
named om-backendapi
|
||||
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
backend "github.com/GoogleCloudPlatform/open-match/examples/backendclient/proto"
|
||||
"github.com/tidwall/gjson"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func bytesToString(data []byte) string {
|
||||
return string(data[:])
|
||||
}
|
||||
|
||||
func ppJSON(s string) {
|
||||
buf := new(bytes.Buffer)
|
||||
json.Indent(buf, []byte(s), "", " ")
|
||||
log.Println(buf)
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Read the profile
|
||||
filename := "profiles/testprofile.json"
|
||||
if len(os.Args) > 1 {
|
||||
filename = os.Args[1]
|
||||
}
|
||||
log.Println("Reading profile from ", filename)
|
||||
jsonFile, err := os.Open(filename)
|
||||
if err != nil {
|
||||
panic("Failed to open file specified at command line. Did you forget to specify one?")
|
||||
}
|
||||
defer jsonFile.Close()
|
||||
|
||||
// parse json data and remove extra whitespace before sending to the backend.
|
||||
jsonData, _ := ioutil.ReadAll(jsonFile) // this reads as a byte array
|
||||
buffer := new(bytes.Buffer) // convert byte array to buffer to send to json.Compact()
|
||||
if err := json.Compact(buffer, jsonData); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
jsonProfile := buffer.String()
|
||||
log.Println("Requesting matches that fit profile:")
|
||||
ppJSON(jsonProfile)
|
||||
//jsonProfile := bytesToString(jsonData)
|
||||
|
||||
// Connect gRPC client
|
||||
ip, err := net.LookupHost("om-backendapi")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial(ip[0]+":50505", grpc.WithInsecure())
|
||||
if err != nil {
|
||||
log.Fatalf("failed to connect: %s", err.Error())
|
||||
}
|
||||
client := backend.NewAPIClient(conn)
|
||||
log.Println("API client connected to", ip[0]+":50505")
|
||||
|
||||
// Test CreateMatch
|
||||
p := &backend.Profile{
|
||||
Id: "test-dm-usc1f",
|
||||
// Make a stub debug hostname from the current time
|
||||
Properties: jsonProfile,
|
||||
}
|
||||
|
||||
//
|
||||
//log.Printf("Looking for matches for profile for the next 5 seconds:")
|
||||
log.Printf("Establishing HTTPv2 stream...")
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
for {
|
||||
log.Println("Attempting to send ListMatches call")
|
||||
stream, err := client.ListMatches(ctx, p)
|
||||
if err != nil {
|
||||
log.Fatalf("Attempting to open stream for ListMatches(_) = _, %v", err)
|
||||
}
|
||||
log.Printf("Waiting for matches...")
|
||||
//for i := 0; i < 2; i++ {
|
||||
for {
|
||||
match, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Error reading stream for ListMatches(_) = _, %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
log.Println("Received match:")
|
||||
ppJSON(match.Properties)
|
||||
if match.Properties == "{error: insufficient_players}" {
|
||||
log.Println("Waiting for a larger player pool...")
|
||||
break
|
||||
}
|
||||
|
||||
// Validate JSON before trying to parse it
|
||||
if !gjson.Valid(string(match.Properties)) {
|
||||
log.Fatal(errors.New("invalid json"))
|
||||
}
|
||||
|
||||
// Get players from the json properties.roster field
|
||||
log.Println("Gathering roster from received match...")
|
||||
players := make([]string, 0)
|
||||
result := gjson.Get(match.Properties, "properties.roster")
|
||||
result.ForEach(func(teamName, teamRoster gjson.Result) bool {
|
||||
teamRoster.ForEach(func(_, player gjson.Result) bool {
|
||||
players = append(players, player.String())
|
||||
return true // keep iterating
|
||||
})
|
||||
return true // keep iterating
|
||||
})
|
||||
//log.Printf("players = %+v\n", players)
|
||||
|
||||
// Assign players in this match to our server
|
||||
log.Println("Assigning players to DGS at example.com:12345")
|
||||
|
||||
playerstr := strings.Join(players, " ")
|
||||
|
||||
roster := &backend.Roster{PlayerIds: playerstr}
|
||||
ci := &backend.ConnectionInfo{ConnectionString: "example.com:12345"}
|
||||
|
||||
assign := &backend.Assignments{Roster: roster, ConnectionInfo: ci}
|
||||
_, err = client.CreateAssignments(context.Background(), assign)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
log.Println("deleting assignments")
|
||||
playerstr = strings.Join(players[0:len(players)/2], " ")
|
||||
roster.PlayerIds = playerstr
|
||||
_, err = client.DeleteAssignments(context.Background(), roster)
|
||||
*/
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user