1# -*- coding: utf-8 -*-
2
3"""
4todo: doc string
5"""
6
7import typing as T
8import dataclasses
9
10from acore_constants.api import ServerLifeCycle
11
12
13@dataclasses.dataclass
14class Server:
15 """
16 Per Game Server configuration.
17
18 :param id: Server id, the naming convention is ``${env_name}-${server_name}``.
19 :param ec2_ami_id: the AMI id for the game server.
20 :param ec2_instance_type: the EC2 instance type for the game server.
21 :param ec2_subnet_id: the EC2 subnet id for the game server.
22 :param ec2_key_name: the EC2 ssh key name for the game server.
23 :param ec2_eip_allocation_id: if you need a static IP, then create
24 an Elastic IP address and put the allocation id here. otherwise,
25 use the automatic public IP address.
26 :param acore_soap_app_version: the acore_soap_app-project git tag for bootstrap.
27 :param acore_server_bootstrap_version: the acore_server_bootstrap-project
28 git tag for bootstrap.
29 :param db_snapshot_id: the snapshot id to create the RDS DB instance.
30 :param db_instance_class: the RDS instance class for the game database.
31 :param db_engine_version: the RDS engine version (all the way to minor).
32 :param db_admin_username: the RDS admin username, usually this is admin.
33 :param db_admin_password: the RDS admin password, we need this password.
34 to create the database user for game server.
35 :param db_username: the database user for game server.
36 :param db_password: the database password for game server.
37 :param lifecycle: the logic "game server (both EC2 and RDS)" lifecycle definition.
38 :param authserver_conf: custom config for authserver.conf.
39 :param worldserver_conf: custom config for worldserver.conf.
40 :param mod_lua_engine_conf: custom config for mod_LuaEngine.conf.
41 """
42
43 id: T.Optional[str] = dataclasses.field(default=None)
44 # EC2 related
45 ec2_ami_id: T.Optional[str] = dataclasses.field(default=None)
46 ec2_instance_type: T.Optional[str] = dataclasses.field(default=None)
47 ec2_subnet_id: T.Optional[str] = dataclasses.field(default=None)
48 ec2_key_name: T.Optional[str] = dataclasses.field(default=None)
49 ec2_eip_allocation_id: T.Optional[str] = dataclasses.field(default=None)
50 acore_soap_app_version: T.Optional[str] = dataclasses.field(default=None)
51 acore_db_app_version: T.Optional[str] = dataclasses.field(default=None)
52 acore_server_bootstrap_version: T.Optional[str] = dataclasses.field(default=None)
53 # RDS related
54 db_snapshot_id: T.Optional[str] = dataclasses.field(default=None)
55 db_instance_class: T.Optional[str] = dataclasses.field(default=None)
56 db_engine_version: T.Optional[str] = dataclasses.field(default=None)
57 db_admin_username: T.Optional[str] = dataclasses.field(default=None)
58 db_admin_password: T.Optional[str] = dataclasses.field(default=None)
59 db_username: T.Optional[str] = dataclasses.field(default=None)
60 db_password: T.Optional[str] = dataclasses.field(default=None)
61 # EC2 and RDS related
62 lifecycle: T.Optional[str] = dataclasses.field(default=None)
63 # authserver.conf, worldserver.conf, ...
64 authserver_conf: T.Dict[str, str] = dataclasses.field(default_factory=dict)
65 worldserver_conf: T.Dict[str, str] = dataclasses.field(default_factory=dict)
66 mod_lua_engine_conf: T.Dict[str, str] = dataclasses.field(default_factory=dict)
67
68 def __post_init__(self):
69 if self.lifecycle not in [
70 ServerLifeCycle.running,
71 ServerLifeCycle.smart_running,
72 ServerLifeCycle.stopped,
73 ServerLifeCycle.deleted,
74 ]: # pragma: no cover
75 raise ValueError(f"{self.lifecycle!r} is not a valid lifecycle definition!")
76
77 def is_ready_for_create_new_server(self) -> bool:
78 """
79 Check if the configuration is sufficient for creating new server.
80
81 See: https://acore-server.readthedocs.io/en/latest/search.html?q=Operation+and+Workflow&check_keywords=yes&area=default#
82 """
83 not_none_fields = [
84 "id",
85 "ec2_ami_id",
86 "ec2_instance_type",
87 "ec2_subnet_id",
88 "ec2_key_name",
89 "acore_soap_app_version",
90 "acore_db_app_version",
91 "acore_server_bootstrap_version",
92 "db_instance_class",
93 "db_engine_version",
94 "db_admin_username",
95 "db_admin_password",
96 "db_username",
97 "db_password",
98 ]
99 for field in not_none_fields:
100 if getattr(self, field) is None:
101 return False
102 return True
103
104 def is_ready_for_create_cloned_server(self) -> bool:
105 """
106 Check if the configuration is sufficient for creating cloned server.
107
108 See: https://acore-server.readthedocs.io/en/latest/search.html?q=Operation+and+Workflow&check_keywords=yes&area=default#
109 """
110 not_none_fields = [
111 "id",
112 "ec2_instance_type",
113 "ec2_subnet_id",
114 "ec2_key_name",
115 "acore_soap_app_version",
116 "acore_db_app_version",
117 "acore_server_bootstrap_version",
118 "db_instance_class",
119 # "db_engine_version", # clone 的时候不需要指定 engine version, 会自动继承
120 # "db_admin_username", # clone 的时候不需要指定 admin username, 会自动继承
121 "db_admin_password",
122 "db_username",
123 "db_password",
124 ]
125 for field in not_none_fields:
126 if getattr(self, field) is None:
127 return False
128 return True
129
130 def is_ready_for_create_updated_server(self) -> bool:
131 """
132 Check if the configuration is sufficient for creating updated server.
133
134 See: https://acore-server.readthedocs.io/en/latest/search.html?q=Operation+and+Workflow&check_keywords=yes&area=default#
135 """
136 not_none_fields = [
137 "id",
138 "ec2_ami_id",
139 "ec2_instance_type",
140 "ec2_subnet_id",
141 "ec2_key_name",
142 "acore_soap_app_version",
143 "acore_db_app_version",
144 "acore_server_bootstrap_version",
145 "db_instance_class",
146 # "db_engine_version", # update server 的时候不需要指定 engine version, 因为我们会用已经存在的数据库
147 # "db_admin_username", # update server 的时候不需要指定 admin username, 因为我们会用已经存在的数据库
148 "db_admin_password",
149 "db_username",
150 "db_password",
151 ]
152 for field in not_none_fields:
153 if getattr(self, field) is None:
154 return False
155 return True
156
157 def is_ready_for_stop_server(self) -> bool:
158 """
159 Check if the configuration is sufficient for stopping server.
160
161 See: https://acore-server.readthedocs.io/en/latest/search.html?q=Operation+and+Workflow&check_keywords=yes&area=default#
162 """
163 not_none_fields = [
164 "id",
165 ]
166 for field in not_none_fields:
167 if getattr(self, field) is None:
168 return False
169 return True
170
171 def is_ready_for_start_server(self) -> bool:
172 """
173 Check if the configuration is sufficient for starting server.
174
175 See: https://acore-server.readthedocs.io/en/latest/search.html?q=Operation+and+Workflow&check_keywords=yes&area=default#
176 """
177 not_none_fields = [
178 "id",
179 ]
180 for field in not_none_fields:
181 if getattr(self, field) is None:
182 return False
183 return True
184
185 def is_ready_for_delete_server(self) -> bool:
186 """
187 Check if the configuration is sufficient for deleting server.
188
189 See: https://acore-server.readthedocs.io/en/latest/search.html?q=Operation+and+Workflow&check_keywords=yes&area=default#
190 """
191 not_none_fields = [
192 "id",
193 ]
194 for field in not_none_fields:
195 if getattr(self, field) is None:
196 return False
197 return True
198
199
200@dataclasses.dataclass
201class ServerMixin:
202 servers: T.Dict[str, Server] = dataclasses.field(default_factory=dict)
203
204 @property
205 def server_blue(self) -> Server:
206 return self.servers["blue"]
207
208 @property
209 def server_green(self) -> Server:
210 return self.servers["green"]
211
212 @property
213 def server_black(self) -> Server:
214 return self.servers["black"]
215
216 @property
217 def server_white(self) -> Server:
218 return self.servers["white"]
219
220 @property
221 def server_yellow(self) -> Server:
222 return self.servers["yello"]
223
224 @property
225 def server_orange(self) -> Server:
226 return self.servers["orange"]