1use super::ActionType;
2use super::DataCenterInfo;
3use super::LeaseInfo;
4use super::Status;
5use serde::de::{Deserialize, Deserializer, Error as DeError, MapAccess, Visitor};
6use serde::ser::{Serialize, SerializeStruct, Serializer};
7use serde_json::{Map, Value};
8use std::fmt;
9
10const INSTANCE: &str = "Instance";
12const HOST_NAME: &str = "hostName";
13const APP: &str = "app";
14const IP_ADDR: &str = "ipAddr";
15const VIP_ADDRESS: &str = "vipAddress";
16const SECURE_VIP_ADDRESS: &str = "secureVipAddress";
17const STATUS: &str = "status";
18const PORT: &str = "port";
19const SECURE_PORT: &str = "securePort";
20const HOME_PAGE_URL: &str = "homePageUrl";
21const STATUS_PAGE_URL: &str = "statusPageUrl";
22const HEALTH_CHECK_URL: &str = "healthCheckUrl";
23const DATA_CENTER_INFO: &str = "dataCenterInfo";
24const LEASE_INFO: &str = "leaseInfo";
25const METADATA: &str = "metadata";
26const OVERRIDDENSTATUS: &str = "overriddenstatus";
27const COUNTRY_ID: &str = "countryId";
28const LAST_UPDATED_TIMESTAMP: &str = "lastUpdatedTimestamp";
29const LAST_DIRTY_TIMESTAMP: &str = "lastDirtyTimestamp";
30const ACTION_TYPE: &str = "actionType";
31const IS_COORDINATED_DISCOVERY_SERVER: &str = "isCoordinatingDiscoveryServer";
32const JSON_FIELDS: &[&str] = &[
33 INSTANCE,
34 HOST_NAME,
35 APP,
36 IP_ADDR,
37 VIP_ADDRESS,
38 SECURE_VIP_ADDRESS,
39 STATUS,
40 PORT,
41 SECURE_PORT,
42 HOME_PAGE_URL,
43 STATUS_PAGE_URL,
44 HEALTH_CHECK_URL,
45 DATA_CENTER_INFO,
46 LEASE_INFO,
47 METADATA,
48 OVERRIDDENSTATUS,
49 COUNTRY_ID,
50 LAST_UPDATED_TIMESTAMP,
51 LAST_DIRTY_TIMESTAMP,
52 ACTION_TYPE,
53 IS_COORDINATED_DISCOVERY_SERVER,
54];
55const RUST_FIELDS: &[&str] = &[
56 "host_name",
57 "app",
58 "ip_addr",
59 "vip_address",
60 "secure_vip_address",
61 "status",
62 "port Option",
63 "secure_port",
64 "homepage_url",
65 "status_page_url",
66 "health_check_url",
67 "data_center_info",
68 "lease_info",
69 "metadata",
70 OVERRIDDENSTATUS,
71 "country_id",
72 "last_updated_timestamp",
73 "last_dirty_timestamp",
74 "action_type",
75 "is_coordinating_discovery_server",
76];
77
78const PORT_DOLLAR: &str = "$";
79const PORT_ENABLED: &str = "@enabled";
80const PORT_FIELDS: &[&str] = &[PORT_DOLLAR, PORT_ENABLED];
81
82#[derive(Debug, PartialEq)]
83pub struct Instance {
84 pub host_name: String,
85 pub app: String,
86 pub ip_addr: String,
87 pub vip_address: String,
88 pub secure_vip_address: String,
89 pub status: Status,
90 pub port: Option<u16>,
91 pub secure_port: Option<u16>,
92 pub homepage_url: String,
93 pub status_page_url: String,
94 pub health_check_url: String,
95 pub data_center_info: DataCenterInfo,
96 pub lease_info: Option<LeaseInfo>,
97 pub metadata: Map<String, Value>,
98 pub overriddenstatus: Option<Status>,
99 pub country_id: u16,
100 pub last_updated_timestamp: i64,
101 pub last_dirty_timestamp: i64,
102 pub action_type: ActionType,
103 pub is_coordinating_discovery_server: bool,
104}
105
106struct Port {
107 port: u16,
108}
109
110impl Port {
111 fn new(port: u16) -> Port {
112 Port { port }
113 }
114}
115
116impl Serialize for Port {
117 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118 where
119 S: Serializer,
120 {
121 let mut s = serializer.serialize_struct("Port", 2)?;
122 s.serialize_field(PORT_DOLLAR, &self.port.to_string())?;
123 s.serialize_field(PORT_ENABLED, "true")?;
124 s.end()
125 }
126}
127
128impl<'de> Deserialize<'de> for Port {
129 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
130 where
131 D: Deserializer<'de>,
132 {
133 enum Field {
134 DollarSign,
135 Enabled,
136 }
137
138 impl<'de> Deserialize<'de> for Field {
139 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
140 where
141 D: Deserializer<'de>,
142 {
143 struct FieldVisitor;
144
145 impl<'de> Visitor<'de> for FieldVisitor {
146 type Value = Field;
147
148 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
149 formatter.write_str("'$' or 'enabled'")
150 }
151 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
152 where
153 E: DeError,
154 {
155 match v {
156 PORT_DOLLAR => Ok(Field::DollarSign),
157 PORT_ENABLED => Ok(Field::Enabled),
158 _ => Err(DeError::unknown_field(v, PORT_FIELDS)),
159 }
160 }
161 }
162 deserializer.deserialize_identifier(FieldVisitor)
163 }
164 }
165
166 struct PortVisitor;
167 impl<'de> Visitor<'de> for PortVisitor {
168 type Value = Port;
169
170 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
171 formatter.write_str("struct Port")
172 }
173
174 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
175 where
176 A: MapAccess<'de>,
177 {
178 let mut maybe_dollar_val: Option<serde_json::Value> = None;
179 let mut maybe_enabled: Option<String> = None;
180
181 while let Some(key) = map.next_key()? {
182 match key {
183 Field::DollarSign => {
184 if maybe_dollar_val.is_some() {
185 return Err(DeError::duplicate_field(PORT_DOLLAR));
186 }
187 maybe_dollar_val = Some(map.next_value()?);
188 }
189 Field::Enabled => {
190 if maybe_enabled.is_some() {
191 return Err(DeError::duplicate_field(PORT_ENABLED));
192 }
193 maybe_enabled = Some(map.next_value()?);
194 }
195 }
196 }
197
198 let dollar = maybe_dollar_val.ok_or_else(|| DeError::missing_field(PORT_DOLLAR))?;
199
200 let dollar_u16 = match dollar {
202 serde_json::Value::Number(n) => n.as_u64().map(|v| v as u16),
203 serde_json::Value::String(s) => s.parse::<u16>().ok(),
204 _ => None,
205 }
206 .ok_or_else(|| DeError::custom("Invalid port value"))?;
207
208 maybe_enabled.ok_or_else(|| DeError::missing_field(PORT_ENABLED))?;
209 Ok(Port::new(dollar_u16))
210 }
211 }
212
213 deserializer.deserialize_struct("Port", PORT_FIELDS, PortVisitor)
214 }
215}
216
217impl Serialize for Instance {
218 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
219 where
220 S: Serializer,
221 {
222 let mut s = serializer.serialize_struct(INSTANCE, 14)?;
223 s.serialize_field(HOST_NAME, &self.host_name)?;
224 s.serialize_field(APP, &self.app)?;
225 s.serialize_field(IP_ADDR, &self.ip_addr)?;
226 s.serialize_field(VIP_ADDRESS, &self.vip_address)?;
227 s.serialize_field(SECURE_VIP_ADDRESS, &self.secure_vip_address)?;
228 s.serialize_field(STATUS, &self.status)?;
229
230 if let &Some(p) = &self.port {
231 let port = Port::new(p);
232 s.serialize_field(PORT, &port)?;
233 }
234
235 if let &Some(p) = &self.secure_port {
236 let port = Port::new(p);
237 s.serialize_field(SECURE_PORT, &port)?;
238 }
239
240 s.serialize_field(HOME_PAGE_URL, &self.homepage_url)?;
241 s.serialize_field(STATUS_PAGE_URL, &self.status_page_url)?;
242 s.serialize_field(HEALTH_CHECK_URL, &self.health_check_url)?;
243 s.serialize_field(DATA_CENTER_INFO, &self.data_center_info)?;
244
245 if let Some(lease_info) = &self.lease_info {
246 s.serialize_field(LEASE_INFO, lease_info)?;
247 }
248
249 if !&self.metadata.is_empty() {
250 s.serialize_field(METADATA, &self.metadata)?;
251 }
252
253 s.serialize_field(COUNTRY_ID, &self.country_id)?;
254
255 if let Some(overridenstatus) = &self.overriddenstatus {
256 s.serialize_field(OVERRIDDENSTATUS, overridenstatus)?;
257 }
258
259 s.serialize_field(
260 IS_COORDINATED_DISCOVERY_SERVER,
261 &self.is_coordinating_discovery_server,
262 )?;
263 s.serialize_field(LAST_UPDATED_TIMESTAMP, &self.last_updated_timestamp)?;
264 s.serialize_field(LAST_DIRTY_TIMESTAMP, &self.last_dirty_timestamp)?;
265 s.serialize_field(ACTION_TYPE, &self.action_type)?;
266
267 s.end()
268 }
269}
270
271impl<'de> Deserialize<'de> for Instance {
272 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
273 where
274 D: Deserializer<'de>,
275 {
276 enum Field {
277 HostName,
278 App,
279 IpAddr,
280 VipAddress,
281 SecureVipAddress,
282 Status,
283 Port,
284 SecurePort,
285 HomepageUrl,
286 StatusPageUrl,
287 HealthCheckUrl,
288 DataCenterInfo,
289 LeaseInfo,
290 Metadata,
291 Overriddenstatus,
292 CountryId,
293 LastUpdatedTimestamp,
294 LastDirtyTimestamp,
295 IsCoordinatingDiscoveryServer,
296 ActionType,
297 }
298
299 impl<'de> Deserialize<'de> for Field {
300 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
301 where
302 D: Deserializer<'de>,
303 {
304 struct FieldVisitor;
305
306 impl<'de> Visitor<'de> for FieldVisitor {
307 type Value = Field;
308
309 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
310 formatter.write_str("An Instance field (see schema)")
311 }
312 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
313 where
314 E: DeError,
315 {
316 match v {
317 HOST_NAME => Ok(Field::HostName),
318 APP => Ok(Field::App),
319 IP_ADDR => Ok(Field::IpAddr),
320 VIP_ADDRESS => Ok(Field::VipAddress),
321 SECURE_VIP_ADDRESS => Ok(Field::SecureVipAddress),
322 STATUS => Ok(Field::Status),
323 PORT => Ok(Field::Port),
324 SECURE_PORT => Ok(Field::SecurePort),
325 HOME_PAGE_URL => Ok(Field::HomepageUrl),
326 STATUS_PAGE_URL => Ok(Field::StatusPageUrl),
327 HEALTH_CHECK_URL => Ok(Field::HealthCheckUrl),
328 DATA_CENTER_INFO => Ok(Field::DataCenterInfo),
329 LEASE_INFO => Ok(Field::LeaseInfo),
330 METADATA => Ok(Field::Metadata),
331 OVERRIDDENSTATUS => Ok(Field::Overriddenstatus),
332 "overriddenStatus" => Ok(Field::Overriddenstatus),
333 COUNTRY_ID => Ok(Field::CountryId),
334 LAST_UPDATED_TIMESTAMP => Ok(Field::LastUpdatedTimestamp),
335 LAST_DIRTY_TIMESTAMP => Ok(Field::LastDirtyTimestamp),
336 IS_COORDINATED_DISCOVERY_SERVER => {
337 Ok(Field::IsCoordinatingDiscoveryServer)
338 }
339 ACTION_TYPE => Ok(Field::ActionType),
340 _ => Err(DeError::unknown_field(v, JSON_FIELDS)),
341 }
342 }
343 }
344
345 deserializer.deserialize_identifier(FieldVisitor)
346 }
347 }
348
349 struct InstanceVisitor;
350
351 impl<'de> Visitor<'de> for InstanceVisitor {
352 type Value = Instance;
353
354 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
355 formatter.write_str("struct Instance")
356 }
357
358 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
359 where
360 A: MapAccess<'de>,
361 {
362 let mut maybe_host_name = None;
363 let mut maybe_app = None;
364 let mut maybe_ip_addr = None;
365 let mut maybe_vip_address = None;
366 let mut maybe_secure_vip_address = None;
367 let mut maybe_status = None;
368 let mut maybe_port: Option<Port> = None;
369 let mut maybe_secure_port: Option<Port> = None;
370 let mut maybe_homepage_url = None;
371 let mut maybe_status_page_url = None;
372 let mut maybe_health_check_url = None;
373 let mut maybe_data_center_info = None;
374 let mut maybe_lease_info = None;
375 let mut maybe_metadata: Option<Map<String, Value>> = None;
376 let mut maybe_overriddenstatus = None;
377 let mut maybe_country_id = None;
378 let mut maybe_last_updated_timestamp = None;
379 let mut maybe_last_dirty_timestamp = None;
380 let mut maybe_is_coordinating_discovery_server = None;
381 let mut maybe_action_type = None;
382
383 while let Some(key) = map.next_key()? {
384 match key {
385 Field::HomepageUrl => {
386 if maybe_homepage_url.is_some() {
387 return Err(DeError::duplicate_field(HOME_PAGE_URL));
388 }
389 maybe_homepage_url = Some(map.next_value()?);
390 }
391 Field::App => {
392 if maybe_app.is_some() {
393 return Err(DeError::duplicate_field(APP));
394 }
395 maybe_app = Some(map.next_value()?);
396 }
397 Field::IpAddr => {
398 if maybe_ip_addr.is_some() {
399 return Err(DeError::duplicate_field(IP_ADDR));
400 }
401 maybe_ip_addr = Some(map.next_value()?);
402 }
403 Field::VipAddress => {
404 if maybe_vip_address.is_some() {
405 return Err(DeError::duplicate_field(VIP_ADDRESS));
406 }
407 maybe_vip_address = Some(map.next_value()?);
408 }
409 Field::SecureVipAddress => {
410 if maybe_secure_vip_address.is_some() {
411 return Err(DeError::duplicate_field(SECURE_VIP_ADDRESS));
412 }
413 maybe_secure_vip_address = Some(map.next_value()?);
414 }
415 Field::Status => {
416 if maybe_status.is_some() {
417 return Err(DeError::duplicate_field(STATUS));
418 }
419 maybe_status = Some(map.next_value()?);
420 }
421 Field::Port => {
422 if maybe_port.is_some() {
423 return Err(DeError::duplicate_field(PORT));
424 }
425 maybe_port = Some(map.next_value()?);
426 }
427 Field::SecurePort => {
428 if maybe_secure_port.is_some() {
429 return Err(DeError::duplicate_field(SECURE_PORT));
430 }
431 maybe_secure_port = Some(map.next_value()?);
432 }
433 Field::StatusPageUrl => {
434 if maybe_status_page_url.is_some() {
435 return Err(DeError::duplicate_field(STATUS_PAGE_URL));
436 }
437 maybe_status_page_url = Some(map.next_value()?);
438 }
439 Field::HealthCheckUrl => {
440 if maybe_health_check_url.is_some() {
441 return Err(DeError::duplicate_field(HEALTH_CHECK_URL));
442 }
443 maybe_health_check_url = Some(map.next_value()?);
444 }
445 Field::DataCenterInfo => {
446 if maybe_data_center_info.is_some() {
447 return Err(DeError::duplicate_field(DATA_CENTER_INFO));
448 }
449 maybe_data_center_info = Some(map.next_value()?);
450 }
451 Field::LeaseInfo => {
452 if maybe_lease_info.is_some() {
453 return Err(DeError::duplicate_field(LEASE_INFO));
454 }
455 maybe_lease_info = Some(map.next_value()?);
456 }
457 Field::Metadata => {
458 if maybe_metadata.is_some() {
459 return Err(DeError::duplicate_field(METADATA));
460 }
461 maybe_metadata = Some(map.next_value()?);
462 }
463 Field::HostName => {
464 if maybe_host_name.is_some() {
465 return Err(DeError::duplicate_field(HOST_NAME));
466 }
467 maybe_host_name = Some(map.next_value()?);
468 }
469 Field::Overriddenstatus => {
470 if maybe_overriddenstatus.is_some() {
471 return Err(DeError::duplicate_field(OVERRIDDENSTATUS));
472 }
473 maybe_overriddenstatus = Some(map.next_value()?);
474 }
475 Field::CountryId => {
476 if maybe_country_id.is_some() {
477 return Err(DeError::duplicate_field(COUNTRY_ID));
478 }
479 let v: serde_json::Value = map.next_value()?;
481 let parsed = match v {
482 serde_json::Value::Number(n) => n.as_u64().map(|x| x as u16),
483 serde_json::Value::String(s) => s.parse::<u16>().ok(),
484 _ => None,
485 };
486 maybe_country_id = parsed;
487 }
488 Field::LastUpdatedTimestamp => {
489 if maybe_last_updated_timestamp.is_some() {
490 return Err(DeError::duplicate_field(LAST_UPDATED_TIMESTAMP));
491 }
492 let v: serde_json::Value = map.next_value()?;
493 let parsed = match v {
494 serde_json::Value::Number(n) => n.as_i64(),
495 serde_json::Value::String(s) => s.parse::<i64>().ok(),
496 _ => None,
497 };
498 maybe_last_updated_timestamp = parsed;
499 }
500 Field::LastDirtyTimestamp => {
501 if maybe_last_dirty_timestamp.is_some() {
502 return Err(DeError::duplicate_field(LAST_DIRTY_TIMESTAMP));
503 }
504 let v: serde_json::Value = map.next_value()?;
505 let parsed = match v {
506 serde_json::Value::Number(n) => n.as_i64(),
507 serde_json::Value::String(s) => s.parse::<i64>().ok(),
508 _ => None,
509 };
510 maybe_last_dirty_timestamp = parsed;
511 }
512 Field::IsCoordinatingDiscoveryServer => {
513 if maybe_is_coordinating_discovery_server.is_some() {
514 return Err(DeError::duplicate_field(
515 IS_COORDINATED_DISCOVERY_SERVER,
516 ));
517 }
518 let v: serde_json::Value = map.next_value()?;
519 let parsed = match v {
520 serde_json::Value::Bool(b) => Some(b),
521 serde_json::Value::String(s) => match s.to_lowercase().as_str() {
522 "true" => Some(true),
523 "false" => Some(false),
524 _ => None,
525 },
526 _ => None,
527 };
528 maybe_is_coordinating_discovery_server = parsed;
529 }
530 Field::ActionType => {
531 if maybe_action_type.is_some() {
532 return Err(DeError::duplicate_field(ACTION_TYPE));
533 }
534 maybe_action_type = Some(map.next_value()?);
535 }
536 }
537 }
538
539 let host_name = maybe_host_name.ok_or_else(|| DeError::missing_field(HOST_NAME));
540 let app = maybe_app.ok_or_else(|| DeError::missing_field(APP));
541 let ip_addr = maybe_ip_addr.ok_or_else(|| DeError::missing_field(IP_ADDR));
542 let vip_address =
543 maybe_vip_address.ok_or_else(|| DeError::missing_field(VIP_ADDRESS));
544 let secure_vip_address = maybe_secure_vip_address
545 .ok_or_else(|| DeError::missing_field(SECURE_VIP_ADDRESS));
546 let status = maybe_status.ok_or_else(|| DeError::missing_field(STATUS));
547 let homepage_url =
548 maybe_homepage_url.ok_or_else(|| DeError::missing_field(HOME_PAGE_URL));
549 let status_page_url =
550 maybe_status_page_url.ok_or_else(|| DeError::missing_field(STATUS_PAGE_URL));
551 let health_check_url =
552 maybe_health_check_url.ok_or_else(|| DeError::missing_field(HEALTH_CHECK_URL));
553 let data_center_info =
554 maybe_data_center_info.ok_or_else(|| DeError::missing_field(DATA_CENTER_INFO));
555 let metadata = maybe_metadata
556 .map(|mut m| {
557 m.remove("@class");
558 m
559 })
560 .unwrap_or_default();
561 let last_updated_timestamp = maybe_last_updated_timestamp
562 .ok_or_else(|| DeError::missing_field(LAST_UPDATED_TIMESTAMP));
563 let last_dirty_timestamp = maybe_last_dirty_timestamp
564 .ok_or_else(|| DeError::missing_field(LAST_DIRTY_TIMESTAMP));
565 let is_coordinating_discovery_server = maybe_is_coordinating_discovery_server
566 .ok_or_else(|| DeError::missing_field(IS_COORDINATED_DISCOVERY_SERVER));
567 let action_type =
568 maybe_action_type.ok_or_else(|| DeError::missing_field(ACTION_TYPE));
569 let country_id = maybe_country_id.ok_or_else(|| DeError::missing_field(COUNTRY_ID));
570
571 Ok(Instance {
572 host_name: host_name?,
573 app: app?,
574 ip_addr: ip_addr?,
575 vip_address: vip_address?,
576 secure_vip_address: secure_vip_address?,
577 status: status?,
578 port: maybe_port.map(|p| p.port),
579 secure_port: maybe_secure_port.map(|p| p.port),
580 homepage_url: homepage_url?,
581 status_page_url: status_page_url?,
582 health_check_url: health_check_url?,
583 data_center_info: data_center_info?,
584 lease_info: maybe_lease_info,
585 metadata,
586 overriddenstatus: maybe_overriddenstatus,
587 country_id: country_id?,
588 last_updated_timestamp: last_updated_timestamp?,
589 last_dirty_timestamp: last_dirty_timestamp?,
590 is_coordinating_discovery_server: is_coordinating_discovery_server?,
591 action_type: action_type?,
592 })
593 }
594 }
595 deserializer.deserialize_struct(INSTANCE, RUST_FIELDS, InstanceVisitor)
596 }
597}
598
599#[cfg(test)]
600pub mod tests {
601 use super::super::AmazonMetaData;
602 use super::super::DcName;
603 use super::*;
604 use serde_json;
605
606 #[test]
607 fn test_instance_serialization() {
608 let json = build_test_instance_json();
609 let instance = build_test_instance();
610 let result = serde_json::to_string(&instance).unwrap();
611
612 assert_eq!(json, result);
618 }
619
620 #[test]
621 fn test_instance_deserialization() {
622 let json = build_test_instance_json();
623 let instance = build_test_instance();
624 let result = serde_json::from_str(&json).unwrap();
625 assert_eq!(instance, result);
626 }
627
628 pub fn build_test_instance_json() -> String {
629 r#"{
630 "hostName": "Foo",
631 "app": "Bar",
632 "ipAddr": "3.128.2.12",
633 "vipAddress": "127.0.0.1",
634 "secureVipAddress": "127.0.0.2",
635 "status": "UP",
636 "port": { "$": "80", "@enabled": "true" },
637 "securePort": { "$": "443", "@enabled": "true" },
638 "homePageUrl": "http://google.com",
639 "statusPageUrl": "http://nytimes.com",
640 "healthCheckUrl": "http://washingtonpost.com",
641 "dataCenterInfo": { "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", "name":"Amazon","metadata":
642 {
643 "ami-launch-index": "001a",
644 "local-hostname": "localhost0",
645 "availability-zone": "US_East1a",
646 "instance-id": "instance1a",
647 "public-ipv4": "32.23.21.212",
648 "public-hostname": "foo.coma",
649 "ami-manifest-path": "/dev/nulla",
650 "local-ipv4": "127.0.0.12",
651 "hostname": "privatefoo.coma",
652 "ami-id": "ami0023",
653 "instance-type": "c4xlarged"
654 }},
655 "leaseInfo": {
656 "renewalIntervalInSecs": 30,
657 "durationInSecs": 90,
658 "registrationTimestamp": 1503442035871,
659 "lastRenewalTimestamp": 1503442035871,
660 "evictionTimestamp": 0,
661 "serviceUpTimestamp": 1503442035721
662 },
663 "metadata": {"something": "somethingelse"},
664 "countryId": 1,
665 "overriddenstatus": "UNKNOWN",
666 "isCoordinatingDiscoveryServer": false,
667 "lastUpdatedTimestamp": 1503442035871,
668 "lastDirtyTimestamp": 1503442035714,
669 "actionType": "ADDED"
670 }"#
671 .to_string()
672 .replace(" ", "")
673 .replace("\n", "")
674 }
675
676 pub fn build_test_instance() -> Instance {
677 let mut metadata = Map::new();
678 metadata.insert(
679 "something".to_owned(),
680 Value::String("somethingelse".to_owned()),
681 );
682 Instance {
683 host_name: "Foo".to_string(),
684 app: "Bar".to_string(),
685 ip_addr: "3.128.2.12".to_string(),
686 vip_address: "127.0.0.1".to_string(),
687 secure_vip_address: "127.0.0.2".to_string(),
688 status: Status::Up,
689 port: Some(80),
690 secure_port: Some(443),
691 homepage_url: "http://google.com".to_string(),
692 status_page_url: "http://nytimes.com".to_string(),
693 health_check_url: "http://washingtonpost.com".to_string(),
694 data_center_info: DataCenterInfo {
695 name: DcName::Amazon,
696 metadata: Some(AmazonMetaData {
697 ami_launch_index: "001a".to_string(),
698 local_hostname: "localhost0".to_string(),
699 availability_zone: "US_East1a".to_string(),
700 instance_id: "instance1a".to_string(),
701 public_ipv4: "32.23.21.212".to_string(),
702 public_hostname: "foo.coma".to_string(),
703 ami_manifest_path: "/dev/nulla".to_string(),
704 local_ipv4: "127.0.0.12".to_string(),
705 hostname: "privatefoo.coma".to_string(),
706 ami_id: "ami0023".to_string(),
707 instance_type: "c4xlarged".to_string(),
708 }),
709 },
710 lease_info: Some(LeaseInfo {
711 renewal_interval_in_secs: 30,
712 duration_in_secs: 90,
713 registration_timestamp: 1503442035871,
714 last_renewal_timestamp: 1503442035871,
715 eviction_timestamp: 0,
716 service_up_timestamp: 1503442035721,
717 }),
718 metadata,
719 overriddenstatus: Some(Status::Unknown),
720 country_id: 1,
721 last_dirty_timestamp: 1503442035714,
722 last_updated_timestamp: 1503442035871,
723 action_type: ActionType::Added,
724 is_coordinating_discovery_server: false,
725 }
726 }
727
728 #[test]
729 fn test_empty_map() {
730 let json = r#"{
731 "hostName": "Foo",
732 "app": "Bar",
733 "ipAddr": "3.128.2.12",
734 "vipAddress": "127.0.0.1",
735 "secureVipAddress": "127.0.0.2",
736 "status": "UP",
737 "port": { "$": "80", "@enabled": "true" },
738 "securePort": { "$": "443", "@enabled": "true" },
739 "homePageUrl": "http://google.com",
740 "statusPageUrl": "http://nytimes.com",
741 "healthCheckUrl": "http://washingtonpost.com",
742 "dataCenterInfo": { "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", "name":"Amazon","metadata":
743 {
744 "ami-launch-index": "001a",
745 "local-hostname": "localhost0",
746 "availability-zone": "US_East1a",
747 "instance-id": "instance1a",
748 "public-ipv4": "32.23.21.212",
749 "public-hostname": "foo.coma",
750 "ami-manifest-path": "/dev/nulla",
751 "local-ipv4": "127.0.0.12",
752 "hostname": "privatefoo.coma",
753 "ami-id": "ami0023",
754 "instance-type": "c4xlarged"
755 }},
756 "leaseInfo": { "renewalIntervalInSecs": 30,
757 "durationInSecs": 90,
758 "registrationTimestamp": 1503442035871,
759 "lastRenewalTimestamp": 1503442035871,
760 "evictionTimestamp": 0,
761 "serviceUpTimestamp": 1503442035721
762 },
763 "metadata": {"@class": "java.util.Collections$EmptyMap"},
764 "countryId": 1,
765 "overriddenstatus": "UNKNOWN",
766 "isCoordinatingDiscoveryServer": false,
767 "lastUpdatedTimestamp": 1503442035871,
768 "lastDirtyTimestamp": 1503442035714,
769 "actionType": "ADDED"
770 }"#;
771
772 let instance: Instance = serde_json::from_str(json).unwrap();
773 assert_eq!(0, instance.metadata.len());
774 }
775}