GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (503)

st2client/st2client/commands/resource.py (8 issues)

1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import os
17
import abc
18
import six
19
import json
20
import logging
21
import httplib
22
from functools import wraps
23
import traceback
24
25
import yaml
26
27
from st2client import commands
28
from st2client.exceptions.operations import OperationFailureException
29
from st2client.formatters import table
30
31
ALLOWED_EXTS = ['.json', '.yaml', '.yml']
32
PARSER_FUNCS = {'.json': json.load, '.yml': yaml.safe_load, '.yaml': yaml.safe_load}
33
LOG = logging.getLogger(__name__)
34
35
36
def add_auth_token_to_kwargs_from_cli(func):
37
    @wraps(func)
38
    def decorate(*args, **kwargs):
39
        ns = args[1]
40
        if getattr(ns, 'token', None):
41
            kwargs['token'] = ns.token
42
        if getattr(ns, 'api_key', None):
43
            kwargs['api_key'] = ns.api_key
44
        return func(*args, **kwargs)
45
    return decorate
46
47
48
class ResourceCommandError(Exception):
49
    pass
50
51
52
class ResourceNotFoundError(Exception):
53
    pass
54
55
56
class ResourceBranch(commands.Branch):
57
58
    def __init__(self, resource, description, app, subparsers,
59
                 parent_parser=None, read_only=False, commands=None,
0 ignored issues
show
Comprehensibility Bug introduced by
commands is re-defining a name which is already available in the outer-scope (previously defined on line 27).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
60
                 has_disable=False):
61
62
        self.resource = resource
63
        super(ResourceBranch, self).__init__(
64
            self.resource.get_alias().lower(), description,
65
            app, subparsers, parent_parser=parent_parser)
66
67
        # Registers subcommands for managing the resource type.
68
        self.subparsers = self.parser.add_subparsers(
69
            help=('List of commands for managing %s.' %
70
                  self.resource.get_plural_display_name().lower()))
71
72
        # Resolves if commands need to be overridden.
73
        commands = commands or {}
74
        cmd_map = {
75
            "list": ResourceListCommand,
76
            "get": ResourceGetCommand,
77
            "create": ResourceCreateCommand,
78
            "update": ResourceUpdateCommand,
79
            "delete": ResourceDeleteCommand,
80
            "enable": ResourceEnableCommand,
81
            "disable": ResourceDisableCommand
82
        }
83
        for cmd, cmd_class in cmd_map.items():
84
            if cmd not in commands:
85
                commands[cmd] = cmd_class
86
87
        # Instantiate commands.
88
        args = [self.resource, self.app, self.subparsers]
89
        self.commands['list'] = commands['list'](*args)
90
        self.commands['get'] = commands['get'](*args)
91
92
        if not read_only:
93
            self.commands['create'] = commands['create'](*args)
94
            self.commands['update'] = commands['update'](*args)
95
            self.commands['delete'] = commands['delete'](*args)
96
97
        if has_disable:
98
            self.commands['enable'] = commands['enable'](*args)
99
            self.commands['disable'] = commands['disable'](*args)
100
101
102
@six.add_metaclass(abc.ABCMeta)
103
class ResourceCommand(commands.Command):
104
    pk_argument_name = None
105
106
    def __init__(self, resource, *args, **kwargs):
107
108
        has_token_opt = kwargs.pop('has_token_opt', True)
109
110
        super(ResourceCommand, self).__init__(*args, **kwargs)
111
112
        self.resource = resource
113
114
        if has_token_opt:
115
            self.parser.add_argument('-t', '--token', dest='token',
116
                                     help='Access token for user authentication. '
117
                                          'Get ST2_AUTH_TOKEN from the environment '
118
                                          'variables by default.')
119
            self.parser.add_argument('--api-key', dest='api_key',
120
                                     help='Api Key for user authentication. '
121
                                          'Get ST2_API_KEY from the environment '
122
                                          'variables by default.')
123
124
        # Formatter flags
125
        self.parser.add_argument('-j', '--json',
126
                                 action='store_true', dest='json',
127
                                 help='Print output in JSON format.')
128
        self.parser.add_argument('-y', '--yaml',
129
                                 action='store_true', dest='yaml',
130
                                 help='Print output in YAML format.')
131
132
    @property
133
    def manager(self):
134
        return self.app.client.managers[self.resource.__name__]
135
136
    @property
137
    def arg_name_for_resource_id(self):
138
        resource_name = self.resource.get_display_name().lower()
139
        return '%s-id' % resource_name.replace(' ', '-')
140
141
    def print_not_found(self, name):
142
        print ('%s "%s" is not found.\n' %
0 ignored issues
show
No space allowed before bracket
print ('s "s" is not found.\n' %
^
Loading history...
143
               (self.resource.get_display_name(), name))
144
145
    def get_resource(self, name_or_id, **kwargs):
146
        pk_argument_name = self.pk_argument_name
147
148
        if pk_argument_name == 'name_or_id':
149
            instance = self.get_resource_by_name_or_id(name_or_id=name_or_id, **kwargs)
150
        elif pk_argument_name == 'ref_or_id':
151
            instance = self.get_resource_by_ref_or_id(ref_or_id=name_or_id, **kwargs)
152
        else:
153
            instance = self.get_resource_by_pk(pk=name_or_id, **kwargs)
154
155
        return instance
156
157
    def get_resource_by_pk(self, pk, **kwargs):
158
        """
159
        Retrieve resource by a primary key.
160
        """
161
        try:
162
            instance = self.manager.get_by_id(pk, **kwargs)
163
        except Exception as e:
164
            traceback.print_exc()
165
            # Hack for "Unauthorized" exceptions, we do want to propagate those
166
            response = getattr(e, 'response', None)
167
            status_code = getattr(response, 'status_code', None)
168
            if status_code and status_code == httplib.UNAUTHORIZED:
169
                raise e
170
171
            instance = None
172
173
        return instance
174
175
    def get_resource_by_id(self, id, **kwargs):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
176
        instance = self.get_resource_by_pk(pk=id, **kwargs)
177
178
        if not instance:
179
            message = ('Resource with id "%s" doesn\'t exist.' % (id))
180
            raise ResourceNotFoundError(message)
181
        return instance
182
183
    def get_resource_by_name(self, name, **kwargs):
184
        """
185
        Retrieve resource by name.
186
        """
187
        instance = self.manager.get_by_name(name, **kwargs)
188
        return instance
189
190
    def get_resource_by_name_or_id(self, name_or_id, **kwargs):
191
        instance = self.get_resource_by_name(name=name_or_id, **kwargs)
192
        if not instance:
193
            instance = self.get_resource_by_pk(pk=name_or_id, **kwargs)
194
195
        if not instance:
196
            message = ('Resource with id or name "%s" doesn\'t exist.' %
197
                       (name_or_id))
198
            raise ResourceNotFoundError(message)
199
        return instance
200
201
    def get_resource_by_ref_or_id(self, ref_or_id, **kwargs):
202
        instance = self.manager.get_by_ref_or_id(ref_or_id=ref_or_id, **kwargs)
203
204
        if not instance:
205
            message = ('Resource with id or reference "%s" doesn\'t exist.' %
206
                       (ref_or_id))
207
            raise ResourceNotFoundError(message)
208
        return instance
209
210
    @abc.abstractmethod
211
    def run(self, args, **kwargs):
212
        raise NotImplementedError
213
214
    @abc.abstractmethod
215
    def run_and_print(self, args, **kwargs):
216
        raise NotImplementedError
217
218
    def _get_metavar_for_argument(self, argument):
219
        return argument.replace('_', '-')
220
221
    def _get_help_for_argument(self, resource, argument):
222
        argument_display_name = argument.title()
223
        resource_display_name = resource.get_display_name().lower()
224
225
        if 'ref' in argument:
226
            result = ('Reference or ID of the %s.' % (resource_display_name))
227
        elif 'name_or_id' in argument:
228
            result = ('Name or ID of the %s.' % (resource_display_name))
229
        else:
230
            result = ('%s of the %s.' % (argument_display_name, resource_display_name))
231
232
        return result
233
234
235
class ResourceTableCommand(ResourceCommand):
236
    display_attributes = ['id', 'name', 'description']
237
238
    def __init__(self, resource, name, description, *args, **kwargs):
239
        super(ResourceTableCommand, self).__init__(resource, name, description,
240
                                                   *args, **kwargs)
241
242
        self.parser.add_argument('-a', '--attr', nargs='+',
243
                                 default=self.display_attributes,
244
                                 help=('List of attributes to include in the '
245
                                       'output. "all" will return all '
246
                                       'attributes.'))
247
        self.parser.add_argument('-w', '--width', nargs='+', type=int,
248
                                 default=None,
249
                                 help=('Set the width of columns in output.'))
250
251
    @add_auth_token_to_kwargs_from_cli
252
    def run(self, args, **kwargs):
253
        return self.manager.get_all(**kwargs)
254
255
    def run_and_print(self, args, **kwargs):
256
        instances = self.run(args, **kwargs)
257
        self.print_output(instances, table.MultiColumnTable,
258
                          attributes=args.attr, widths=args.width,
259
                          json=args.json, yaml=args.yaml)
260
261
262
class ResourceListCommand(ResourceTableCommand):
263
    def __init__(self, resource, *args, **kwargs):
264
        super(ResourceListCommand, self).__init__(
265
            resource, 'list', 'Get the list of %s.' % resource.get_plural_display_name().lower(),
266
            *args, **kwargs)
267
268
269
class ContentPackResourceListCommand(ResourceListCommand):
270
    """
271
    Base command class for use with resources which belong to a content pack.
272
    """
273
    def __init__(self, resource, *args, **kwargs):
274
        super(ContentPackResourceListCommand, self).__init__(resource,
275
                                                             *args, **kwargs)
276
277
        self.parser.add_argument('-p', '--pack', type=str,
278
                                 help=('Only return resources belonging to the'
279
                                       ' provided pack'))
280
281
    @add_auth_token_to_kwargs_from_cli
282
    def run(self, args, **kwargs):
283
        filters = {'pack': args.pack}
284
        filters.update(**kwargs)
285
        return self.manager.get_all(**filters)
286
287
288
class ResourceGetCommand(ResourceCommand):
289
    display_attributes = ['all']
290
    attribute_display_order = ['id', 'name', 'description']
291
292
    pk_argument_name = 'name_or_id'  # name of the attribute which stores resource PK
293
294
    help_string = None
295
296
    def __init__(self, resource, *args, **kwargs):
297
        super(ResourceGetCommand, self).__init__(
298
            resource, 'get',
299
            self.help_string or 'Get individual %s.' % resource.get_display_name().lower(),
300
            *args, **kwargs
301
        )
302
303
        argument = self.pk_argument_name
304
        metavar = self._get_metavar_for_argument(argument=self.pk_argument_name)
305
        help = self._get_help_for_argument(resource=resource,
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in help.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
306
                                           argument=self.pk_argument_name)
307
308
        self.parser.add_argument(argument,
309
                                 metavar=metavar,
310
                                 help=help)
311
        self.parser.add_argument('-a', '--attr', nargs='+',
312
                                 default=self.display_attributes,
313
                                 help=('List of attributes to include in the '
314
                                       'output. "all" or unspecified will '
315
                                       'return all attributes.'))
316
317
    @add_auth_token_to_kwargs_from_cli
318
    def run(self, args, **kwargs):
319
        resource_id = getattr(args, self.pk_argument_name, None)
320
        return self.get_resource_by_id(resource_id, **kwargs)
321
322
    def run_and_print(self, args, **kwargs):
323
        try:
324
            instance = self.run(args, **kwargs)
325
            self.print_output(instance, table.PropertyValueTable,
326
                              attributes=args.attr, json=args.json, yaml=args.yaml,
327
                              attribute_display_order=self.attribute_display_order)
328
        except ResourceNotFoundError:
329
            resource_id = getattr(args, self.pk_argument_name, None)
330
            self.print_not_found(resource_id)
331
            raise OperationFailureException('Resource %s not found.' % resource_id)
332
333
334
class ContentPackResourceGetCommand(ResourceGetCommand):
335
    """
336
    Command for retrieving a single resource which belongs to a content pack.
337
338
    Note: All the resources which belong to the content pack can either be
339
    retrieved by a reference or by an id.
340
    """
341
342
    attribute_display_order = ['id', 'pack', 'name', 'description']
343
344
    pk_argument_name = 'ref_or_id'
345
346
    def get_resource(self, ref_or_id, **kwargs):
347
        return self.get_resource_by_ref_or_id(ref_or_id=ref_or_id, **kwargs)
348
349
350
class ResourceCreateCommand(ResourceCommand):
351
352
    def __init__(self, resource, *args, **kwargs):
353
        super(ResourceCreateCommand, self).__init__(resource, 'create',
354
            'Create a new %s.' % resource.get_display_name().lower(),
355
            *args, **kwargs)
356
357
        self.parser.add_argument('file',
358
                                 help=('JSON/YAML file containing the %s to create.'
359
                                       % resource.get_display_name().lower()))
360
361
    @add_auth_token_to_kwargs_from_cli
362
    def run(self, args, **kwargs):
363
        data = load_meta_file(args.file)
364
        instance = self.resource.deserialize(data)
365
        return self.manager.create(instance, **kwargs)
366
367
    def run_and_print(self, args, **kwargs):
368
        try:
369
            instance = self.run(args, **kwargs)
370
            if not instance:
371
                raise Exception('Server did not create instance.')
372
            self.print_output(instance, table.PropertyValueTable,
373
                              attributes=['all'], json=args.json, yaml=args.yaml)
374
        except Exception as e:
375
            message = e.message or str(e)
376
            print('ERROR: %s' % (message))
377
            raise OperationFailureException(message)
378
379
380
class ResourceUpdateCommand(ResourceCommand):
381
    pk_argument_name = 'name_or_id'
382
383
    def __init__(self, resource, *args, **kwargs):
384
        super(ResourceUpdateCommand, self).__init__(resource, 'update',
385
            'Updating an existing %s.' % resource.get_display_name().lower(),
386
            *args, **kwargs)
387
388
        argument = self.pk_argument_name
389
        metavar = self._get_metavar_for_argument(argument=self.pk_argument_name)
390
        help = self._get_help_for_argument(resource=resource,
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in help.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
391
                                           argument=self.pk_argument_name)
392
393
        self.parser.add_argument(argument,
394
                                 metavar=metavar,
395
                                 help=help)
396
        self.parser.add_argument('file',
397
                                 help=('JSON/YAML file containing the %s to update.'
398
                                       % resource.get_display_name().lower()))
399
400
    @add_auth_token_to_kwargs_from_cli
401
    def run(self, args, **kwargs):
402
        resource_id = getattr(args, self.pk_argument_name, None)
403
        instance = self.get_resource(resource_id, **kwargs)
404
        data = load_meta_file(args.file)
405
        modified_instance = self.resource.deserialize(data)
406
407
        if not getattr(modified_instance, 'id', None):
408
            modified_instance.id = instance.id
409
        else:
410
            if modified_instance.id != instance.id:
411
                raise Exception('The value for the %s id in the JSON/YAML file '
412
                                'does not match the ID provided in the '
413
                                'command line arguments.' %
414
                                self.resource.get_display_name().lower())
415
        return self.manager.update(modified_instance, **kwargs)
416
417
    def run_and_print(self, args, **kwargs):
418
        instance = self.run(args, **kwargs)
419
        try:
420
            self.print_output(instance, table.PropertyValueTable,
421
                              attributes=['all'], json=args.json, yaml=args.yaml)
422
        except Exception as e:
423
            print('ERROR: %s' % e.message)
424
            raise OperationFailureException(e.message)
425
426
427
class ContentPackResourceUpdateCommand(ResourceUpdateCommand):
428
    pk_argument_name = 'ref_or_id'
429
430
431
class ResourceEnableCommand(ResourceCommand):
432
    pk_argument_name = 'name_or_id'
433
434
    def __init__(self, resource, *args, **kwargs):
435
        super(ResourceEnableCommand, self).__init__(resource, 'enable',
436
            'Enable an existing %s.' % resource.get_display_name().lower(),
437
            *args, **kwargs)
438
439
        argument = self.pk_argument_name
440
        metavar = self._get_metavar_for_argument(argument=self.pk_argument_name)
441
        help = self._get_help_for_argument(resource=resource,
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in help.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
442
                                           argument=self.pk_argument_name)
443
444
        self.parser.add_argument(argument,
445
                                 metavar=metavar,
446
                                 help=help)
447
448
    @add_auth_token_to_kwargs_from_cli
449
    def run(self, args, **kwargs):
450
        resource_id = getattr(args, self.pk_argument_name, None)
451
        instance = self.get_resource(resource_id, **kwargs)
452
453
        data = instance.serialize()
454
455
        if 'ref' in data:
456
            del data['ref']
457
458
        data['enabled'] = True
459
        modified_instance = self.resource.deserialize(data)
460
461
        return self.manager.update(modified_instance, **kwargs)
462
463
    def run_and_print(self, args, **kwargs):
464
        instance = self.run(args, **kwargs)
465
        self.print_output(instance, table.PropertyValueTable,
466
                          attributes=['all'], json=args.json, yaml=args.yaml)
467
468
469
class ContentPackResourceEnableCommand(ResourceEnableCommand):
470
    pk_argument_name = 'ref_or_id'
471
472
473
class ResourceDisableCommand(ResourceCommand):
474
    pk_argument_name = 'name_or_id'
475
476
    def __init__(self, resource, *args, **kwargs):
477
        super(ResourceDisableCommand, self).__init__(resource, 'disable',
478
            'Disable an existing %s.' % resource.get_display_name().lower(),
479
            *args, **kwargs)
480
481
        argument = self.pk_argument_name
482
        metavar = self._get_metavar_for_argument(argument=self.pk_argument_name)
483
        help = self._get_help_for_argument(resource=resource,
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in help.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
484
                                           argument=self.pk_argument_name)
485
486
        self.parser.add_argument(argument,
487
                                 metavar=metavar,
488
                                 help=help)
489
490
    @add_auth_token_to_kwargs_from_cli
491
    def run(self, args, **kwargs):
492
        resource_id = getattr(args, self.pk_argument_name, None)
493
        instance = self.get_resource(resource_id, **kwargs)
494
495
        data = instance.serialize()
496
497
        if 'ref' in data:
498
            del data['ref']
499
500
        data['enabled'] = False
501
        modified_instance = self.resource.deserialize(data)
502
503
        return self.manager.update(modified_instance, **kwargs)
504
505
    def run_and_print(self, args, **kwargs):
506
        instance = self.run(args, **kwargs)
507
        self.print_output(instance, table.PropertyValueTable,
508
                          attributes=['all'], json=args.json, yaml=args.yaml)
509
510
511
class ContentPackResourceDisableCommand(ResourceDisableCommand):
512
    pk_argument_name = 'ref_or_id'
513
514
515
class ResourceDeleteCommand(ResourceCommand):
516
    pk_argument_name = 'name_or_id'
517
518
    def __init__(self, resource, *args, **kwargs):
519
        super(ResourceDeleteCommand, self).__init__(resource, 'delete',
520
            'Delete an existing %s.' % resource.get_display_name().lower(),
521
            *args, **kwargs)
522
523
        argument = self.pk_argument_name
524
        metavar = self._get_metavar_for_argument(argument=self.pk_argument_name)
525
        help = self._get_help_for_argument(resource=resource,
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in help.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
526
                                           argument=self.pk_argument_name)
527
528
        self.parser.add_argument(argument,
529
                                 metavar=metavar,
530
                                 help=help)
531
532
    @add_auth_token_to_kwargs_from_cli
533
    def run(self, args, **kwargs):
534
        resource_id = getattr(args, self.pk_argument_name, None)
535
        instance = self.get_resource(resource_id, **kwargs)
536
        self.manager.delete(instance, **kwargs)
537
538
    def run_and_print(self, args, **kwargs):
539
        resource_id = getattr(args, self.pk_argument_name, None)
540
541
        try:
542
            self.run(args, **kwargs)
543
            print('Resource with id "%s" has been successfully deleted.' % (resource_id))
544
        except ResourceNotFoundError:
545
            self.print_not_found(resource_id)
546
            raise OperationFailureException('Resource %s not found.' % resource_id)
547
548
549
class ContentPackResourceDeleteCommand(ResourceDeleteCommand):
550
    """
551
    Base command class for deleting a resource which belongs to a content pack.
552
    """
553
554
    pk_argument_name = 'ref_or_id'
555
556
557
def load_meta_file(file_path):
558
    if not os.path.isfile(file_path):
559
        raise Exception('File "%s" does not exist.' % file_path)
560
561
    file_name, file_ext = os.path.splitext(file_path)
562
    if file_ext not in ALLOWED_EXTS:
563
        raise Exception('Unsupported meta type %s, file %s. Allowed: %s' %
564
                        (file_ext, file_path, ALLOWED_EXTS))
565
566
    with open(file_path, 'r') as f:
567
        return PARSER_FUNCS[file_ext](f)
568