|
132 | 132 |
|
133 | 133 | MODULE_TO_TYPE_MAPPINGS = MODULE_TO_TYPE_MAPPINGS |
134 | 134 |
|
| 135 | +LIST_NOT_ALL_ITEMS_RETURNED_WARNING = "WARNING: This operation supports pagination and not all resources were returned. Re-run using the --all option to auto paginate and list all resources." |
| 136 | + |
135 | 137 |
|
136 | 138 | def override(key, default): |
137 | 139 | return OVERRIDES.get(key, default) |
@@ -304,6 +306,19 @@ def render(data, headers, ctx, display_all_headers=False, nest_data_in_data_attr |
304 | 306 | if key is not 'data': |
305 | 307 | click.echo('{}: {}'.format(key, display_dictionary[key]), file=sys.stderr) |
306 | 308 |
|
| 309 | + # print out a notice if not all results were returned, and the operation supports the --all parameter |
| 310 | + if headers and headers.get('opc-next-page'): |
| 311 | + has_all_param = False |
| 312 | + if ctx.command.params: |
| 313 | + for param in ctx.command.params: |
| 314 | + if param.name == 'all_pages': |
| 315 | + has_all_param = True |
| 316 | + break |
| 317 | + |
| 318 | + if has_all_param: |
| 319 | + notice = LIST_NOT_ALL_ITEMS_RETURNED_WARNING |
| 320 | + click.echo(click.style(notice, fg='red'), file=sys.stderr) |
| 321 | + |
307 | 322 |
|
308 | 323 | def print_table(data): |
309 | 324 | table_data = [] |
@@ -428,6 +443,13 @@ def wrapped_call(ctx, *args, **kwargs): |
428 | 443 | if 'missing_required_parameters' in ctx.obj: |
429 | 444 | raise cli_exceptions.RequiredValueNotInDefaultOrUserInputError('Missing option(s) --{}.'.format(', --'.join(ctx.obj['missing_required_parameters']))) |
430 | 445 |
|
| 446 | + # check this AFTER checking for required params |
| 447 | + # if there are missing required params we want to show that notice, not prompt the user for deletion confirmation |
| 448 | + if 'prompt_for_deletion' in ctx.obj and ctx.obj['prompt_for_deletion']: |
| 449 | + value = click.confirm("Are you sure you want to delete this resource?") |
| 450 | + if not value: |
| 451 | + ctx.abort() |
| 452 | + |
431 | 453 | func(ctx, *args, **kwargs) |
432 | 454 | except exceptions.ServiceError as exception: |
433 | 455 | if exception.status == 401: |
@@ -681,11 +703,8 @@ def confirmation_callback(ctx, param, value): |
681 | 703 | if not ctx.obj['generate_full_command_json_input'] and not ctx.obj['generate_param_json_input']: |
682 | 704 | # if --force was supplied we don't want to prompt |
683 | 705 | if not value: |
684 | | - value = click.confirm("Are you sure you want to delete this resource?") |
685 | | - if not value: |
686 | | - ctx.abort() |
687 | | - |
688 | | - return value |
| 706 | + # propmt for deletion after reading ALL params, because it is unnecessary if we are missing required params |
| 707 | + ctx.obj['prompt_for_deletion'] = True |
689 | 708 |
|
690 | 709 |
|
691 | 710 | confirm_delete_option = click.option( |
@@ -1111,9 +1130,9 @@ def warn_on_invalid_file_permissions(filepath): |
1111 | 1130 | if is_windows(): |
1112 | 1131 | windows_warn_on_invalid_file_permissions(filepath) |
1113 | 1132 | else: |
1114 | | - # validate that permissions are user RW only (600) |
1115 | | - user_rw_perms = oct(384) # 600 |
1116 | | - if not oct(stat.S_IMODE(os.lstat(filepath).st_mode)) == user_rw_perms: |
| 1133 | + # validate that permissions are user R or RW only (400 or 600) |
| 1134 | + unwanted_perms = 127 # octal 177 |
| 1135 | + if (stat.S_IMODE(os.lstat(filepath).st_mode) & unwanted_perms): |
1117 | 1136 | warning = 'WARNING: Permissions on {filepath} are too open. To fix this please execute the following command: oci setup repair-file-permissions --file {filepath} '.format(filepath=filepath) |
1118 | 1137 | click.echo(click.style(warning, fg='red'), file=sys.stderr) |
1119 | 1138 |
|
@@ -1194,20 +1213,85 @@ def handle_optional_param(ctx, param, value): |
1194 | 1213 | return _coalesce_param(ctx, param, value, False) |
1195 | 1214 |
|
1196 | 1215 |
|
1197 | | -def _coalesce_param(ctx, param, value, required): |
| 1216 | +def handle_param_with_default(required, default): |
| 1217 | + def internal_handle_param(ctx, param, value): |
| 1218 | + return _coalesce_param(ctx, param, value, required, explicit_default=default) |
| 1219 | + |
| 1220 | + return internal_handle_param |
| 1221 | + |
| 1222 | + |
| 1223 | +def _coalesce_param(ctx, param, value, required, explicit_default=None): |
| 1224 | + # if value is populated (from an explicit argument), use that |
| 1225 | + # options with multiple=True with no value explicitly given will be passed as '()' so in that case we want to check defaults file |
| 1226 | + if value is not None and value != (): |
| 1227 | + return value |
| 1228 | + |
1198 | 1229 | hyphenated_param_name = param.name.replace('_', '-') |
1199 | 1230 | try: |
| 1231 | + value = None |
1200 | 1232 | if isinstance(param.type, click.types.File) and value is None: |
1201 | | - return get_click_file_from_default_values_file(ctx, hyphenated_param_name, param.type.mode, required) |
| 1233 | + value = get_click_file_from_default_values_file(ctx, hyphenated_param_name, param.type.mode, required) |
1202 | 1234 | else: |
1203 | | - return coalesce_provided_and_default_value(ctx, hyphenated_param_name, value, required) |
| 1235 | + value = coalesce_provided_and_default_value(ctx, hyphenated_param_name, value, required) |
| 1236 | + |
| 1237 | + if value is None and explicit_default is not None: |
| 1238 | + value = explicit_default |
| 1239 | + |
| 1240 | + return value |
1204 | 1241 | except cli_exceptions.RequiredValueNotInDefaultOrUserInputError: |
| 1242 | + # if there is an explicit default then its not missing so just return explicit_default |
| 1243 | + if explicit_default is not None: |
| 1244 | + return explicit_default |
| 1245 | + |
1205 | 1246 | if 'missing_required_parameters' not in ctx.obj: |
1206 | 1247 | ctx.obj['missing_required_parameters'] = [] |
1207 | 1248 |
|
1208 | 1249 | ctx.obj['missing_required_parameters'].append(hyphenated_param_name) |
1209 | 1250 |
|
1210 | 1251 |
|
| 1252 | +def option(*param_decls, **attrs): |
| 1253 | + """Attaches an option to the command. All positional arguments are |
| 1254 | + passed as parameter declarations to :class:`Option`; all keyword |
| 1255 | + arguments are forwarded unchanged (except ``cls``). |
| 1256 | + This is equivalent to creating an :class:`Option` instance manually |
| 1257 | + and attaching it to the :attr:`Command.params` list. |
| 1258 | +
|
| 1259 | + :param cls: the option class to instantiate. This defaults to |
| 1260 | + :class:`Option`. |
| 1261 | + """ |
| 1262 | + def decorator(f): |
| 1263 | + default = None |
| 1264 | + # remove default from option declaration because it will override defaults file |
| 1265 | + if 'default' in attrs: |
| 1266 | + default = attrs['default'] |
| 1267 | + del attrs['default'] |
| 1268 | + |
| 1269 | + # add default value to help text |
| 1270 | + if 'help' in attrs and 'show_default' in attrs and attrs['show_default']: |
| 1271 | + spacer = '' if attrs['help'].endswith(' ') else ' ' |
| 1272 | + attrs['help'] = '{}{}{}'.format(attrs['help'], spacer, '[default: {}]'.format(str(default))) |
| 1273 | + |
| 1274 | + required = False |
| 1275 | + if 'required' in attrs and attrs['required'] and 'help' in attrs: |
| 1276 | + required = True |
| 1277 | + # add [required] to help text for this param |
| 1278 | + if 'help' in attrs: |
| 1279 | + spacer = '' if attrs['help'].endswith(' ') else ' ' |
| 1280 | + attrs['help'] = '{}{}{}'.format(attrs['help'], spacer, '[required]') |
| 1281 | + |
| 1282 | + # for click purposes mark everything as optional so our default file lookup logic still has a chance to run |
| 1283 | + del attrs['required'] |
| 1284 | + |
| 1285 | + # don't allow 'callback' because it will conflict with the required / optional param callback we add |
| 1286 | + if 'callback' in attrs: |
| 1287 | + raise ValueError('Cannot specify callback function for option, conflicts with default callback.') |
| 1288 | + |
| 1289 | + attrs.setdefault('callback', handle_param_with_default(required, default)) |
| 1290 | + |
| 1291 | + return click.option(*param_decls, **attrs)(f) |
| 1292 | + return decorator |
| 1293 | + |
| 1294 | + |
1211 | 1295 | # Decodes a byte string using stdout's encoding if we can get it, otherwise decode using the Python default |
1212 | 1296 | def _try_decode_using_stdout(output): |
1213 | 1297 | if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding is not None: |
|
0 commit comments