rust_eureka/response/
instance.rs

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