a method for updating the properties of a bucket in S3

        :param bucket_name: string with name of bucket
        :param access_control: string with type of access control policy
        :param version_control: [optional] boolean to enable versioning of records
        :param log_destination: [optional] dictionary with bucket name and prefix of log bucket
        :param lifecycle_rules: [optional] list of dictionaries with rules for aging data
        :param tag_list: [optional] list of dictionaries with key and value for tag
        :param notification_settings: [optional] list of dictionaries with notification details
        :param region_replication: [optional] dictionary with replication settings (WIP)
        :param access_policy: [optional] dictionary with policy for user access (WIP)
        :return: list of dictionaries with changes to bucket

def update_bucket(self, bucket_name, access_control='private', version_control=False, log_destination=None, lifecycle_rules=None, tag_list=None, notification_settings=None, region_replication=None, access_policy=None):

        '''
            a method for updating the properties of a bucket in S3

        :param bucket_name: string with name of bucket
        :param access_control: string with type of access control policy
        :param version_control: [optional] boolean to enable versioning of records
        :param log_destination: [optional] dictionary with bucket name and prefix of log bucket
        :param lifecycle_rules: [optional] list of dictionaries with rules for aging data
        :param tag_list: [optional] list of dictionaries with key and value for tag
        :param notification_settings: [optional] list of dictionaries with notification details
        :param region_replication: [optional] dictionary with replication settings (WIP)
        :param access_policy: [optional] dictionary with policy for user access (WIP)
        :return: list of dictionaries with changes to bucket
        '''

        title = '%s.update_bucket' % self.__class__.__name__
        
    # validate inputs
        input_fields = {
            'bucket_name': bucket_name,
            'access_control': access_control,
            'version_control': version_control,
            'log_destination': log_destination,
            'lifecycle_rules': lifecycle_rules,
            'tag_list': tag_list,
            'notification_settings': notification_settings,
            'region_replication': region_replication,
            'access_policy': access_policy
        }
        for key, value in input_fields.items():
            if value:
                object_title = '%s(%s=%s)' % (title, key, str(value))
                self.fields.validate(value, '.%s' % key, object_title)
        if log_destination == None:
            input_fields['log_destination'] = {}
        if lifecycle_rules == None:
            input_fields['lifecycle_rules'] = []
        if tag_list == None:
            input_fields['tag_list'] = []
        if notification_settings == None:
            input_fields['notification_settings'] = []
        if region_replication == None:
            input_fields['region_replication'] = {}
        if access_policy == None:
            input_fields['access_policy'] = {}

    # verify requirements and limits
        self.list_buckets()
        if not bucket_name in self.bucket_list:
            raise ValueError('S3 bucket "%s" does not exist in aws region %s. Update not applicable.' % (bucket_name, self.iam.region_name))
        if log_destination:
            log_name = log_destination['name']
            if not log_name in self.bucket_list:
                raise ValueError('S3 Bucket "%s" for logging does not exist in aws region %s.' % (log_name, self.iam.region_name))
            else:
                log_details = self.read_bucket(log_name)
                if log_details['access_control'] != 'log-delivery-write':
                    raise ValueError('S3 Bucket "%s" for logging does not have "log-delivery-write" access control.' % log_name)
            if not 'prefix' in log_destination.keys():
                input_fields['log_destination']['prefix'] = ''

    # TODO: check to see if required notification arns exist
        if notification_settings:
            for notification in notification_settings:
                arn_id = notification['arn']
    
    # retrieve existing bucket fields
        existing_fields = self.read_bucket(bucket_name)

    # alphabetize tag list
        if existing_fields['tag_list']:
            existing_fields['tag_list'] = sorted(existing_fields['tag_list'], key=lambda k: k['key'])
        if input_fields['tag_list']:
            input_fields['tag_list'] = sorted(input_fields['tag_list'], key=lambda k: k['key'])

    # determine difference between new and old versions
        from labpack.parsing.comparison import compare_records
        change_list = compare_records(input_fields, existing_fields)
        if not change_list:
            self.iam.printer('There are no changes to make to bucket "%s".' % bucket_name)
            return change_list
        
    # process changes
        self.iam.printer('Updating bucket "%s".' % bucket_name, flush=True)
        processed_list = []
        for change in change_list:

    # replace access control
            if change['path'][0] == 'access_control' and 'access_control' not in processed_list:
                kw_args = {
                    'Bucket': bucket_name,
                    'ACL': input_fields['access_control']
                }
                try:
                    self.connection.put_bucket_acl(**kw_args)
                    self.iam.printer('.', flush=True)
                except:
                    raise AWSConnectionError(title)
            processed_list.append('access_control')

    # replace version control
            if change['path'][0] == 'version_control' and 'version_control' not in processed_list:
                if input_fields['version_control']:
                    try:
                        self.connection.put_bucket_versioning(
                            Bucket=bucket_name,
                            VersioningConfiguration={ 'Status': 'Enabled' }
                        )
                        self.iam.printer('.', flush=True)
                    except:
                        raise AWSConnectionError(title)
                else:
                    try:
                        self.connection.put_bucket_versioning(
                            Bucket=bucket_name,
                            VersioningConfiguration={ 'Status': 'Suspended' }
                        )
                        self.iam.printer('.', flush=True)
                    except:
                        raise AWSConnectionError(title)
                processed_list.append('version_control')

    # replace log destination
            if change['path'][0] == 'log_destination' and 'log_destination' not in processed_list:
                if input_fields['log_destination']:
                    log_name = input_fields['log_destination']['name']
                    log_prefix = input_fields['log_destination']['prefix']
                    kw_args = {
                        'Bucket': bucket_name,
                        'BucketLoggingStatus': {
                            'LoggingEnabled': {
                                'TargetBucket': log_name
                            }
                        }
                    }
                    if log_prefix:
                        kw_args['BucketLoggingStatus']['LoggingEnabled']['TargetPrefix'] = log_prefix
                else:
                    kw_args = {
                        'Bucket': bucket_name,
                        'BucketLoggingStatus': {}
                    }
                try:
                    self.connection.put_bucket_logging(**kw_args)
                    self.iam.printer('.', flush=True)
                except:
                    raise AWSConnectionError(title)
                processed_list.append('log_destination')

    # replace lifecycle rules
            if change['path'][0] == 'lifecycle_rules' and 'lifecycle_rules' not in processed_list:
                if input_fields['lifecycle_rules']:
                    kw_args = {
                        'Bucket': bucket_name,
                        'LifecycleConfiguration': { 'Rules': [ ] }
                    }
                    for rule in input_fields['lifecycle_rules']:
                        details = {
                            'Prefix': rule['prefix'],
                            'Status': 'Enabled'
                        }
                        if rule['action'] == 'archive':
                            if rule['current_version']:
                                details['Transition'] = {
                                    'Days': rule['longevity'],
                                    'StorageClass': 'GLACIER'
                                }
                            else:
                                details['NoncurrentVersionTransition'] = {
                                    'NoncurrentDays': rule['longevity'],
                                    'StorageClass': 'GLACIER'
                                }
                        else:
                            if rule['current_version']:
                                details['Expiration'] = { 'Days': rule['longevity'] }
                            else:
                                details['NoncurrentVersionExpiration'] = { 'NoncurrentDays': rule['longevity'] }
                        kw_args['LifecycleConfiguration']['Rules'].append(details)
                    try:
                        self.connection.put_bucket_lifecycle(**kw_args)
                        self.iam.printer('.', flush=True)
                    except:
                        raise AWSConnectionError(title)
                else:
                    try:
                        self.connection.delete_bucket_lifecycle( Bucket=bucket_name )
                        self.iam.printer('.', flush=True)
                    except:
                        raise AWSConnectionError(title)
                processed_list.append('lifecycle_rules')

    # replace bucket tags
            if change['path'][0] == 'tag_list' and 'tag_list' not in processed_list:
                if input_fields['tag_list']:
                    try:
                        self.connection.put_bucket_tagging(
                            Bucket=bucket_name,
                            Tagging={ 'TagSet': self.iam.prepare(input_fields['tag_list']) }
                        )
                        self.iam.printer('.', flush=True)
                    except:
                        raise AWSConnectionError(title)
                else:
                    try:
                        self.connection.delete_bucket_tagging( Bucket=bucket_name )
                        self.iam.printer('.', flush=True)
                    except:
                        raise AWSConnectionError(title)
                processed_list.append('tag_list')

    # replace notification settings
            if change['path'][0] == 'notification_settings' and 'notification_settings' not in processed_list:
                kw_args = {
                    'Bucket': bucket_name,
                    'NotificationConfiguration': {}
                }
                if input_fields['notification_settings']:
                    for notification in input_fields['notification_settings']:
                        details = {
                            'Events': [],
                            'Filter': { 'Key': { 'FilterRules': [] } }
                        }
                        details['Events'].append(notification['event'])
                        if notification['filters']:
                            for key, value in notification['filters'].items():
                                filter_details = {
                                    'Name': key,
                                    'Value': value
                                }
                                details['Filter']['Key']['FilterRules'].append(filter_details)
                        if notification['service'] == 'sns':
                            details['TopicArn'] = notification['arn']
                            if not 'TopicConfigurations' in kw_args['NotificationConfiguration']:
                                kw_args['NotificationConfiguration']['TopicConfigurations'] = []
                            kw_args['NotificationConfiguration']['TopicConfigurations'].append(details)
                        elif notification['service'] == 'sqs':
                            details['QueueArn'] = notification['arn']
                            if not 'QueueConfigurations' in kw_args['NotificationConfiguration']:
                                kw_args['NotificationConfiguration']['QueueConfigurations'] = []
                            kw_args['NotificationConfiguration']['QueueConfigurations'].append(details)
                        elif notification['service'] == 'lambda':
                            if not 'LambdaFunctionConfigurations' in kw_args['NotificationConfiguration']:
                                kw_args['NotificationConfiguration']['LambdaFunctionConfigurations'] = []
                            details['LambdaFunctionArn'] = notification['arn']
                            kw_args['NotificationConfiguration']['LambdaFunctionConfigurations'].append(details)
                try:
                    # TODO: response = self.connection.put_bucket_notification_configuration(**kw_args)
                    self.iam.printer('.', flush=True)
                except:
                    raise AWSConnectionError(title)
                processed_list.append('notification_settings')

    # TODO: replace region replication
            if change['path'][0] == 'region_replication' and 'region_replication' not in processed_list:
                if input_fields['region_replication']:
                    pass
                else:
                    pass
                processed_list.append('region_replication')

    # TODO: replace access policy
            if change['path'][0] == 'access_policy' and 'access_policy' not in processed_list:
                if input_fields['access_policy']:
                    pass
                else:
                    pass
                processed_list.append('access_policy')

    # report and return change list
        self.iam.printer(' done.')
        return change_list