1# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"). You 4# may not use this file except in compliance with the License. A copy of 5# the License is located at 6# 7# http://aws.amazon.com/apache2.0/ 8# 9# or in the "license" file accompanying this file. This file is 10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11# ANY KIND, either express or implied. See the License for the specific 12# language governing permissions and limitations under the License. 13import os 14import logging 15 16from botocore.exceptions import ProfileNotFound 17 18from awscli.compat import compat_input 19from awscli.customizations.commands import BasicCommand 20from awscli.customizations.configure.addmodel import AddModelCommand 21from awscli.customizations.configure.set import ConfigureSetCommand 22from awscli.customizations.configure.get import ConfigureGetCommand 23from awscli.customizations.configure.list import ConfigureListCommand 24from awscli.customizations.configure.writer import ConfigFileWriter 25 26from . import mask_value, profile_to_section 27 28 29logger = logging.getLogger(__name__) 30 31 32def register_configure_cmd(cli): 33 cli.register('building-command-table.main', 34 ConfigureCommand.add_command) 35 36 37class InteractivePrompter(object): 38 39 def get_value(self, current_value, config_name, prompt_text=''): 40 if config_name in ('aws_access_key_id', 'aws_secret_access_key'): 41 current_value = mask_value(current_value) 42 response = compat_input("%s [%s]: " % (prompt_text, current_value)) 43 if not response: 44 # If the user hits enter, we return a value of None 45 # instead of an empty string. That way we can determine 46 # whether or not a value has changed. 47 response = None 48 return response 49 50 51class ConfigureCommand(BasicCommand): 52 NAME = 'configure' 53 DESCRIPTION = BasicCommand.FROM_FILE() 54 SYNOPSIS = ('aws configure [--profile profile-name]') 55 EXAMPLES = ( 56 'To create a new configuration::\n' 57 '\n' 58 ' $ aws configure\n' 59 ' AWS Access Key ID [None]: accesskey\n' 60 ' AWS Secret Access Key [None]: secretkey\n' 61 ' Default region name [None]: us-west-2\n' 62 ' Default output format [None]:\n' 63 '\n' 64 'To update just the region name::\n' 65 '\n' 66 ' $ aws configure\n' 67 ' AWS Access Key ID [****]:\n' 68 ' AWS Secret Access Key [****]:\n' 69 ' Default region name [us-west-1]: us-west-2\n' 70 ' Default output format [None]:\n' 71 ) 72 SUBCOMMANDS = [ 73 {'name': 'list', 'command_class': ConfigureListCommand}, 74 {'name': 'get', 'command_class': ConfigureGetCommand}, 75 {'name': 'set', 'command_class': ConfigureSetCommand}, 76 {'name': 'add-model', 'command_class': AddModelCommand} 77 ] 78 79 # If you want to add new values to prompt, update this list here. 80 VALUES_TO_PROMPT = [ 81 # (logical_name, config_name, prompt_text) 82 ('aws_access_key_id', "AWS Access Key ID"), 83 ('aws_secret_access_key', "AWS Secret Access Key"), 84 ('region', "Default region name"), 85 ('output', "Default output format"), 86 ] 87 88 def __init__(self, session, prompter=None, config_writer=None): 89 super(ConfigureCommand, self).__init__(session) 90 if prompter is None: 91 prompter = InteractivePrompter() 92 self._prompter = prompter 93 if config_writer is None: 94 config_writer = ConfigFileWriter() 95 self._config_writer = config_writer 96 97 def _run_main(self, parsed_args, parsed_globals): 98 # Called when invoked with no args "aws configure" 99 new_values = {} 100 # This is the config from the config file scoped to a specific 101 # profile. 102 try: 103 config = self._session.get_scoped_config() 104 except ProfileNotFound: 105 config = {} 106 for config_name, prompt_text in self.VALUES_TO_PROMPT: 107 current_value = config.get(config_name) 108 new_value = self._prompter.get_value(current_value, config_name, 109 prompt_text) 110 if new_value is not None and new_value != current_value: 111 new_values[config_name] = new_value 112 config_filename = os.path.expanduser( 113 self._session.get_config_variable('config_file')) 114 if new_values: 115 profile = self._session.profile 116 self._write_out_creds_file_values(new_values, profile) 117 if profile is not None: 118 section = profile_to_section(profile) 119 new_values['__section__'] = section 120 self._config_writer.update_config(new_values, config_filename) 121 122 def _write_out_creds_file_values(self, new_values, profile_name): 123 # The access_key/secret_key are now *always* written to the shared 124 # credentials file (~/.aws/credentials), see aws/aws-cli#847. 125 # post-conditions: ~/.aws/credentials will have the updated credential 126 # file values and new_values will have the cred vars removed. 127 credential_file_values = {} 128 if 'aws_access_key_id' in new_values: 129 credential_file_values['aws_access_key_id'] = new_values.pop( 130 'aws_access_key_id') 131 if 'aws_secret_access_key' in new_values: 132 credential_file_values['aws_secret_access_key'] = new_values.pop( 133 'aws_secret_access_key') 134 if credential_file_values: 135 if profile_name is not None: 136 credential_file_values['__section__'] = profile_name 137 shared_credentials_filename = os.path.expanduser( 138 self._session.get_config_variable('credentials_file')) 139 self._config_writer.update_config( 140 credential_file_values, 141 shared_credentials_filename) 142