Skip to content

Commit 10e83a7

Browse files
committed
Option to specify profile
1 parent 8cb9b23 commit 10e83a7

File tree

2 files changed

+48
-13
lines changed

2 files changed

+48
-13
lines changed

S3/Config.py

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -261,18 +261,19 @@ class Config(object):
261261
max_retries = 5
262262

263263
## Creating a singleton
264-
def __new__(self, configfile = None, access_key=None, secret_key=None, access_token=None):
264+
def __new__(self, configfile = None, access_key=None, secret_key=None, access_token=None, profile=None):
265265
if self._instance is None:
266266
self._instance = object.__new__(self)
267267
return self._instance
268268

269-
def __init__(self, configfile = None, access_key=None, secret_key=None, access_token=None):
269+
def __init__(self, configfile = None, access_key=None, secret_key=None, access_token=None, profile=None):
270270
if configfile:
271+
debug("Config: Initializing with config file '%s', profile '%s'" % (configfile, profile))
271272
try:
272-
self.read_config_file(configfile)
273+
self.read_config_file(configfile, profile)
273274
except IOError:
274275
if 'AWS_SHARED_CREDENTIALS_FILE' in os.environ or 'AWS_CREDENTIAL_FILE' in os.environ or 'AWS_PROFILE' in os.environ:
275-
self.aws_credential_file()
276+
self.aws_credential_file(profile)
276277

277278
# override these if passed on the command-line
278279
# Allow blank secret_key
@@ -335,7 +336,7 @@ def role_config(self):
335336
'%s=%s' % (k, s3_quote(v, unicode_output=True))
336337
for k, v in params.items()
337338
])
338-
sts_endpoint = os.environ.get("AWS_STS_ENDPOINT", sts_endpoint)
339+
sts_endpoint = os.environ.get("AWS_STS_ENDPOINT", sts_endpoint)
339340
if os.environ.get("AWS_STS_REGIONAL_ENDPOINTS") == "regional":
340341
# Check if the AWS_REGION variable is available to use as a region.
341342
region = os.environ.get("AWS_REGION")
@@ -441,7 +442,7 @@ def role_refresh(self):
441442
except Exception:
442443
warning("Could not refresh role")
443444

444-
def aws_credential_file(self):
445+
def aws_credential_file(self, profile=None):
445446
try:
446447
aws_credential_file = os.path.expanduser('~/.aws/credentials')
447448
credential_file_from_env = os.environ.get('AWS_SHARED_CREDENTIALS_FILE') \
@@ -450,6 +451,7 @@ def aws_credential_file(self):
450451
os.path.isfile(credential_file_from_env):
451452
aws_credential_file = base_unicodise(credential_file_from_env)
452453
elif not os.path.isfile(aws_credential_file):
454+
debug("AWS credentials file not found at %s" % aws_credential_file)
453455
return
454456

455457
config = PyConfigParser()
@@ -482,7 +484,12 @@ def aws_credential_file(self):
482484
"Error reading aws_credential_file "
483485
"(%s): %s" % (aws_credential_file, str(exc)))
484486

485-
profile = base_unicodise(os.environ.get('AWS_PROFILE', "default"))
487+
# Determine which profile to use based on priority:
488+
# 1. Command-line option (profile parameter)
489+
# 2. Environment variable (AWS_PROFILE)
490+
# 3. Default to 'default' if neither is specified
491+
if profile is None:
492+
profile = base_unicodise(os.environ.get('AWS_PROFILE', "default"))
486493
debug("Using AWS profile '%s'" % (profile))
487494

488495
# get_key - helper function to read the aws profile credentials
@@ -559,8 +566,23 @@ def option_list(self):
559566
retval.append(option)
560567
return retval
561568

562-
def read_config_file(self, configfile):
563-
cp = ConfigParser(configfile)
569+
def read_config_file(self, configfile, profile=None):
570+
# Determine which profile to use based on priority:
571+
# 1. Command-line option (profile parameter)
572+
# 2. Environment variable (AWS_PROFILE)
573+
# 3. Default to 'default' if neither is specified
574+
if profile is None:
575+
profile = os.environ.get('AWS_PROFILE', 'default')
576+
577+
# Use the determined profile as the section name
578+
sections = [profile]
579+
cp = ConfigParser(configfile, sections)
580+
581+
# If the specified profile section doesn't exist,
582+
# fall back to the default section
583+
if not cp.cfg:
584+
cp = ConfigParser(configfile, ["default"])
585+
564586
for option in self.option_list():
565587
_option = cp.get(option)
566588
if _option is not None:
@@ -655,20 +677,29 @@ def parse_file(self, file, sections = []):
655677
debug("ConfigParser: Reading file '%s'" % file)
656678
if type(sections) != type([]):
657679
sections = [sections]
658-
in_our_section = True
680+
681+
if sections:
682+
debug("ConfigParser: Looking for sections: %s" % sections)
683+
else:
684+
debug("ConfigParser: Reading default section")
685+
686+
in_our_section = len(sections) == 0 # If no sections specified, read all
687+
current_section = "default"
659688
r_comment = re.compile(r'^\s*#.*')
660689
r_empty = re.compile(r'^\s*$')
661690
r_section = re.compile(r'^\[([^\]]+)\]')
662691
r_data = re.compile(r'^\s*(?P<key>\w+)\s*=\s*(?P<value>.*)')
663692
r_quotes = re.compile(r'^"(.*)"\s*$')
693+
664694
with io.open(file, "r", encoding=self.get('encoding', 'UTF-8')) as fp:
665695
for line in fp:
666696
if r_comment.match(line) or r_empty.match(line):
667697
continue
668698
is_section = r_section.match(line)
669699
if is_section:
670-
section = is_section.groups()[0]
671-
in_our_section = (section in sections) or (len(sections) == 0)
700+
current_section = is_section.groups()[0]
701+
in_our_section = (current_section in sections) or (len(sections) == 0)
702+
debug("ConfigParser: Found section '%s', reading: %s" % (current_section, in_our_section))
672703
continue
673704
is_data = r_data.match(line)
674705
if is_data and in_our_section:
@@ -682,6 +713,9 @@ def parse_file(self, file, sections = []):
682713
print_value = data["value"]
683714
debug("ConfigParser: %s->%s" % (data["key"], print_value))
684715
continue
716+
if is_data and not in_our_section:
717+
# Skip data in sections we're not interested in
718+
continue
685719
warning("Ignoring invalid line in '%s': %s" % (file, line))
686720

687721
def __getitem__(self, name):

s3cmd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3158,6 +3158,7 @@ def main():
31583158
optparser.add_option( "--access_key", dest="access_key", help="AWS Access Key")
31593159
optparser.add_option( "--secret_key", dest="secret_key", help="AWS Secret Key")
31603160
optparser.add_option( "--access_token", dest="access_token", help="AWS Access Token")
3161+
optparser.add_option( "--profile", dest="profile", metavar="PROFILE", help="Profile to use from .s3cfg file")
31613162

31623163
optparser.add_option("-n", "--dry-run", dest="dry_run", action="store_true", help="Only show what should be uploaded or downloaded but don't actually do it. May still perform S3 requests to get bucket listings and other information though (only for file transfer commands)")
31633164

@@ -3316,7 +3317,7 @@ def main():
33163317
sys.exit(EX_CONFIG)
33173318

33183319
try:
3319-
cfg = Config(options.config, options.access_key, options.secret_key, options.access_token)
3320+
cfg = Config(options.config, options.access_key, options.secret_key, options.access_token, options.profile)
33203321
except ValueError as exc:
33213322
raise ParameterError(unicode(exc))
33223323
except IOError as e:

0 commit comments

Comments
 (0)