Update resource from uri to filename on local system.

        Update means three things:
        1. GET resources
        2. set mtime in local time to be equal to timestamp in UTC (should perhaps
        or at least warn if different from LastModified from the GET response instead
        but maybe warn if different (or just earlier than) the lastmod we expected
        from the resource list
        3. check that resource matches expected information

        Also update self.last_timestamp if the timestamp (in source frame) of this
        resource is later and the current value.

        Returns the number of resources updated/created (0 or 1)

def update_resource(self, resource, filename, change=None):
        """Update resource from uri to filename on local system.

        Update means three things:
        1. GET resources
        2. set mtime in local time to be equal to timestamp in UTC (should perhaps
        or at least warn if different from LastModified from the GET response instead
        but maybe warn if different (or just earlier than) the lastmod we expected
        from the resource list
        3. check that resource matches expected information

        Also update self.last_timestamp if the timestamp (in source frame) of this
        resource is later and the current value.

        Returns the number of resources updated/created (0 or 1)
        """
        path = os.path.dirname(filename)
        distutils.dir_util.mkpath(path)
        num_updated = 0
        if (self.dryrun):
            self.logger.info(
                "dryrun: would GET %s --> %s" %
                (resource.uri, filename))
        else:
            # 1. GET
            for try_i in range(1, self.tries + 1):
                try:
                    r = requests.get(resource.uri, timeout=self.timeout, stream=True)
                    # Fail on 4xx or 5xx
                    r.raise_for_status()
                    with open(filename, 'wb') as fd:
                        for chunk in r.iter_content(chunk_size=1024):
                            fd.write(chunk)
                    num_updated += 1
                    break
                except requests.Timeout as e:
                    if try_i < self.tries:
                        msg = 'Download timed out, retrying...'
                        self.logger.info(msg)
                        # Continue loop
                    else:
                        # No more tries left, so fail
                        msg = "Failed to GET %s after %s tries -- %s" % (resource.uri, self.tries, str(e))
                        if (self.ignore_failures):
                            self.logger.warning(msg)
                            return(num_updated)
                        else:
                            raise ClientFatalError(msg)
                except (requests.RequestException, IOError) as e:
                    msg = "Failed to GET %s -- %s" % (resource.uri, str(e))
                    if (self.ignore_failures):
                        self.logger.warning(msg)
                        return(num_updated)
                    else:
                        raise ClientFatalError(msg)
            # 2. set timestamp if we have one
            if (resource.timestamp is not None):
                unixtime = int(resource.timestamp)  # no fractional
                os.utime(filename, (unixtime, unixtime))
                if (resource.timestamp > self.last_timestamp):
                    self.last_timestamp = resource.timestamp
            self.log_event(Resource(resource=resource, change=change))
            # 3. sanity check
            length = os.stat(filename).st_size
            if (resource.length is not None and resource.length != length):
                self.logger.info(
                    "Downloaded size for %s of %d bytes does not match expected %d bytes" %
                    (resource.uri, length, resource.length))
            if (len(self.hashes) > 0):
                self.check_hashes(filename, resource)
        return(num_updated)