# Buggy code
def query(
    key,
    keyid,
    method="GET",
    params=None,
    headers=None,
    requesturl=None,
    return_url=False,
    bucket=None,
    service_url=None,
    path="",
    return_bin=False,
    action=None,
    local_file=None,
    verify_ssl=True,
    location=None,
    full_headers=False,
    role_arn=None,
    chunk_size=16384,
):
    """
    Perform a query against an S3-like API. This function requires that a
    secret key and the id for that key are passed in. For instance:

        s3.keyid: GKTADJGHEIQSXMKKRBJ08H
        s3.key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs

    If keyid or key is not specified, an attempt to fetch them from EC2 IAM
    metadata service will be made.

    A service_url may also be specified in the configuration:

        s3.service_url: s3.amazonaws.com

    If a service_url is not specified, the default is s3.amazonaws.com. This
    may appear in various documentation as an "endpoint". A comprehensive list
    for Amazon S3 may be found at::

        http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region

    The service_url will form the basis for the final endpoint that is used to
    query the service.

    SSL verification may also be turned off in the configuration:

    s3.verify_ssl: False

    This is required if using S3 bucket names that contain a period, as
    these will not match Amazon's S3 wildcard certificates. Certificate
    verification is enabled by default.

    A region may be specified:

        s3.location: eu-central-1

    If region is not specified, an attempt to fetch the region from EC2 IAM
    metadata service will be made. Failing that, default is us-east-1
    """
    if not HAS_REQUESTS:
        log.error("There was an error: requests is required for s3 access")

    if not headers:
        headers = {}

    if not params:
        params = {}

    if not service_url:
        service_url = "s3.amazonaws.com"

    if bucket:
        endpoint = "{0}.{1}".format(bucket, service_url)
    else:
        endpoint = service_url

    # Try grabbing the credentials from the EC2 instance IAM metadata if available
    if not key:
        key = salt.utils.aws.IROLE_CODE

    if not keyid:
        keyid = salt.utils.aws.IROLE_CODE

    if not location:
        location = __utils__["aws.get_location"]()

    data = ""
    payload_hash = None
    if method == "PUT":
        if local_file:
            payload_hash = salt.utils.get_hash(local_file, form="sha256")

    if not requesturl:
        requesturl = "https://{0}/{1}".format(endpoint, path)
        headers, requesturl = salt.utils.aws.sig4(
            method,
            endpoint,
            params,
            data=data,
            uri="/{0}".format(path),
            prov_dict={"id": keyid, "key": key},
            role_arn=role_arn,
            location=location,
            product="s3",
            requesturl=requesturl,
            payload_hash=payload_hash,
        )

    log.debug("S3 Request: {0}".format(requesturl))
    log.debug("S3 Headers::")
    log.debug("    Authorization: {0}".format(headers["Authorization"]))

    if not data:
        data = None

    response = None
    if method == "PUT":
        if local_file:
            with salt.utils.fopen(local_file, "r") as data:
                result = requests.request(
                    method,
                    requesturl,
                    headers=headers,
                    data=data,
                    verify=verify_ssl,
                    stream=True,
                )
        response = result.content
    elif method == "GET" and local_file and not return_bin:
        result = requests.request(
            method,
            requesturl,
            headers=headers,
            data=data,
            verify=verify_ssl,
            stream=True,
        )
    else:
        result = requests.request(
            method, requesturl, headers=headers, data=data, verify=verify_ssl
        )
        response = result.content
    if result.status_code >= 400:
        # On error the S3 API response should contain error message
        log.debug("    Response content: {0}".format(response))

    log.debug("S3 Response Status Code: {0}".format(result.status_code))

    if method == "PUT":
        if result.status_code == 200:
            if local_file:
                log.debug("Uploaded from {0} to {1}".format(local_file, path))
            else:
                log.debug("Created bucket {0}".format(bucket))
        else:
            if local_file:
                log.debug(
                    "Failed to upload from {0} to {1}: {2}".format(
                        local_file,
                        path,
                        result.status_code,
                    )
                )
            else:
                log.debug("Failed to create bucket {0}".format(bucket))
        return

    if method == "DELETE":
        if str(result.status_code).startswith("2"):
            if path:
                log.debug("Deleted {0} from bucket {1}".format(path, bucket))
            else:
                log.debug("Deleted bucket {0}".format(bucket))
        else:
            if path:
                log.debug(
                    "Failed to delete {0} from bucket {1}: {2}".format(
                        path,
                        bucket,
                        result.status_code,
                    )
                )
            else:
                log.debug("Failed to delete bucket {0}".format(bucket))
        return

    # This can be used to save a binary object to disk
    if local_file and method == "GET":
        log.debug("Saving to local file: {0}".format(local_file))
        with salt.utils.fopen(local_file, "wb") as out:
            for chunk in result.iter_content(chunk_size=chunk_size):
                out.write(chunk)
        return "Saved to local file: {0}".format(local_file)

    # This can be used to return a binary object wholesale
    if return_bin:
        return response

    if response:
        items = ET.fromstring(response)

        ret = []
        for item in items:
            ret.append(xml.to_dict(item))

        if return_url is True:
            return ret, requesturl
    else:
        if result.status_code != requests.codes.ok:
            return
        ret = {"headers": []}
        if full_headers:
            ret["headers"] = dict(result.headers)
        else:
            for header in result.headers:
                ret["headers"].append(header.strip())

    return ret


# Traceback (NameError)
2016-11-01 14:17:21,492 [salt.fileserver  ][DEBUG   ][11090] Updating roots fileserver cache
2016-11-01 14:17:21,499 [salt.fileserver  ][DEBUG   ][11090] Updating s3fs fileserver cache
2016-11-01 14:17:21,500 [salt.loaded.int.fileserver.s3fs][DEBUG   ][11090] Refreshing buckets cache file
2016-11-01 14:17:21,500 [salt.daemons.masterapi][ERROR   ][11090] Exception global name '__utils__' is not defined occurred in file server update
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/salt/daemons/masterapi.py", line 270, in fileserver_update
fileserver.update()
File "/usr/local/lib/python2.7/dist-packages/salt/fileserver/__init__.py", line 458, in update
self.servers[fstr]()
File "/usr/local/lib/python2.7/dist-packages/salt/fileserver/s3fs.py", line 102, in update
metadata = _init()
File "/usr/local/lib/python2.7/dist-packages/salt/fileserver/s3fs.py", line 348, in _init
metadata = _refresh_buckets_cache_file(cache_file)
File "/usr/local/lib/python2.7/dist-packages/salt/fileserver/s3fs.py", line 456, in _refresh_buckets_cache_file
s3_meta = __get_s3_meta(bucket_name)
File "/usr/local/lib/python2.7/dist-packages/salt/fileserver/s3fs.py", line 410, in __get_s3_meta
return_bin=False)
File "/usr/local/lib/python2.7/dist-packages/salt/utils/s3.py", line 100, in query
location = __utils__['aws.get_location']()
NameError: global name '__utils__' is not defined

# Fixed code
def query(
    key,
    keyid,
    method="GET",
    params=None,
    headers=None,
    requesturl=None,
    return_url=False,
    bucket=None,
    service_url=None,
    path="",
    return_bin=False,
    action=None,
    local_file=None,
    verify_ssl=True,
    location=None,
    full_headers=False,
    role_arn=None,
    chunk_size=16384,
):
    """
    Perform a query against an S3-like API. This function requires that a
    secret key and the id for that key are passed in. For instance:

        s3.keyid: GKTADJGHEIQSXMKKRBJ08H
        s3.key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs

    If keyid or key is not specified, an attempt to fetch them from EC2 IAM
    metadata service will be made.

    A service_url may also be specified in the configuration:

        s3.service_url: s3.amazonaws.com

    If a service_url is not specified, the default is s3.amazonaws.com. This
    may appear in various documentation as an "endpoint". A comprehensive list
    for Amazon S3 may be found at::

        http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region

    The service_url will form the basis for the final endpoint that is used to
    query the service.

    SSL verification may also be turned off in the configuration:

    s3.verify_ssl: False

    This is required if using S3 bucket names that contain a period, as
    these will not match Amazon's S3 wildcard certificates. Certificate
    verification is enabled by default.

    A region may be specified:

        s3.location: eu-central-1

    If region is not specified, an attempt to fetch the region from EC2 IAM
    metadata service will be made. Failing that, default is us-east-1
    """
    if not HAS_REQUESTS:
        log.error("There was an error: requests is required for s3 access")

    if not headers:
        headers = {}

    if not params:
        params = {}

    if not service_url:
        service_url = "s3.amazonaws.com"

    if bucket:
        endpoint = "{0}.{1}".format(bucket, service_url)
    else:
        endpoint = service_url

    # Try grabbing the credentials from the EC2 instance IAM metadata if available
    if not key:
        key = salt.utils.aws.IROLE_CODE

    if not keyid:
        keyid = salt.utils.aws.IROLE_CODE

    if not location:
        location = salt.utils.aws.get_location()

    data = ""
    payload_hash = None
    if method == "PUT":
        if local_file:
            payload_hash = salt.utils.get_hash(local_file, form="sha256")

    if not requesturl:
        requesturl = "https://{0}/{1}".format(endpoint, path)
        headers, requesturl = salt.utils.aws.sig4(
            method,
            endpoint,
            params,
            data=data,
            uri="/{0}".format(path),
            prov_dict={"id": keyid, "key": key},
            role_arn=role_arn,
            location=location,
            product="s3",
            requesturl=requesturl,
            payload_hash=payload_hash,
        )

    log.debug("S3 Request: {0}".format(requesturl))
    log.debug("S3 Headers::")
    log.debug("    Authorization: {0}".format(headers["Authorization"]))

    if not data:
        data = None

    response = None
    if method == "PUT":
        if local_file:
            with salt.utils.fopen(local_file, "r") as data:
                result = requests.request(
                    method,
                    requesturl,
                    headers=headers,
                    data=data,
                    verify=verify_ssl,
                    stream=True,
                )
        response = result.content
    elif method == "GET" and local_file and not return_bin:
        result = requests.request(
            method,
            requesturl,
            headers=headers,
            data=data,
            verify=verify_ssl,
            stream=True,
        )
    else:
        result = requests.request(
            method, requesturl, headers=headers, data=data, verify=verify_ssl
        )
        response = result.content
    if result.status_code >= 400:
        # On error the S3 API response should contain error message
        log.debug("    Response content: {0}".format(response))

    log.debug("S3 Response Status Code: {0}".format(result.status_code))

    if method == "PUT":
        if result.status_code == 200:
            if local_file:
                log.debug("Uploaded from {0} to {1}".format(local_file, path))
            else:
                log.debug("Created bucket {0}".format(bucket))
        else:
            if local_file:
                log.debug(
                    "Failed to upload from {0} to {1}: {2}".format(
                        local_file,
                        path,
                        result.status_code,
                    )
                )
            else:
                log.debug("Failed to create bucket {0}".format(bucket))
        return

    if method == "DELETE":
        if str(result.status_code).startswith("2"):
            if path:
                log.debug("Deleted {0} from bucket {1}".format(path, bucket))
            else:
                log.debug("Deleted bucket {0}".format(bucket))
        else:
            if path:
                log.debug(
                    "Failed to delete {0} from bucket {1}: {2}".format(
                        path,
                        bucket,
                        result.status_code,
                    )
                )
            else:
                log.debug("Failed to delete bucket {0}".format(bucket))
        return

    # This can be used to save a binary object to disk
    if local_file and method == "GET":
        log.debug("Saving to local file: {0}".format(local_file))
        with salt.utils.fopen(local_file, "wb") as out:
            for chunk in result.iter_content(chunk_size=chunk_size):
                out.write(chunk)
        return "Saved to local file: {0}".format(local_file)

    # This can be used to return a binary object wholesale
    if return_bin:
        return response

    if response:
        items = ET.fromstring(response)

        ret = []
        for item in items:
            ret.append(xml.to_dict(item))

        if return_url is True:
            return ret, requesturl
    else:
        if result.status_code != requests.codes.ok:
            return
        ret = {"headers": []}
        if full_headers:
            ret["headers"] = dict(result.headers)
        else:
            for header in result.headers:
                ret["headers"].append(header.strip())

    return ret