Example #1
0
    def from_file(cls, jira_manager: 'JiraManager', config_file_name: str,
                  team_manager: 'TeamManager') -> 'JiraView':
        """
        Throws ConfigError on error
        """
        config_file = JiraView._build_config(config_file_name)
        if not os.path.isfile(config_file):
            raise ConfigError(
                'Failed to find jira view config file: {}'.format(config_file))

        config_parser = configparser.RawConfigParser()
        config_parser.read(config_file)

        name = config_parser.get('JiraView', 'name')
        jira_connection = jira_manager.get_jira_connection(
            config_parser.get('JiraView', 'jira_connection'))

        new_jira_view = JiraView(name, jira_connection)
        # Due to init order dep, we init teams empty here then fill them out separately from team_manager
        if config_parser.has_option('JiraView', 'Teams'):
            for t in config_parser.get('JiraView', 'Teams').split(','):
                new_jira_view._teams[t] = team_manager.get_team_by_name(t)

        sections = config_parser.sections()
        for section_name in sections:
            if section_name == 'JiraView':
                continue
            new_jira_filter = JiraFilter.from_file(jira_manager, section_name,
                                                   config_parser)
            new_jira_view.add_raw_filter(new_jira_filter)

        return new_jira_view
Example #2
0
    def save_connection_config(self) -> None:
        if self.name and self.url:
            config_parser = RawConfigParser()
            config_parser.add_section(SECTION_TITLE)
            config_parser.set(SECTION_TITLE, 'url', self.url)

            if self._requires_auth:
                config_parser.set(SECTION_TITLE, 'username',
                                  self._auth['username'])
                password = encode(encode_password(), self._auth['password'])
                config_parser.set(SECTION_TITLE, 'password', password)

            if self.jenkins_views:
                config_parser.set(SECTION_TITLE, 'views',
                                  ','.join(self.view_names))

                for view in self.views:
                    view.save_view_config()

            save_argus_config(
                config_parser,
                build_config_file(jenkins_connections_dir, self.name))
        else:
            raise ConfigError(
                'No data to save in JenkinsConnection config file.')
Example #3
0
    def from_file(cls, connection_name: str) -> Optional['JiraConnection']:
        """
        Raises ConfigError if file is missing, internal data fails validation, or assignee list fails to be queried
        """
        config_file = cls._build_config(connection_name)
        if not os.path.isfile(config_file):
            raise ConfigError(
                'Cannot initialize JIRA instance: {}. Missing config file: {}'.
                format(connection_name, cls._build_config(connection_name)))

        try:
            cp = configparser.RawConfigParser()
            cp.read(cls._build_config(connection_name))
            url = cp.get('Connection', 'url').rstrip('/')
            user = decode(encode_password(), cp.get('Connection', 'user'))
            password = decode(encode_password(),
                              cp.get('Connection', 'password'))

            result = JiraConnection(connection_name, url, user, password)
            result.possible_projects = cp.get('Connection',
                                              'projects').split(',')

            return result
        except configparser.NoOptionError as e:
            print('Failed to create JiraConnection from file: {}. Error: {}'.
                  format(config_file, str(e)))
            return None
Example #4
0
    def __init__(self,
                 connection_name='unknown',
                 url='unknown',
                 user_name='unknown',
                 password='******') -> None:
        self.possible_projects = []  # type: List[str]

        self.connection_name = connection_name
        self._url = url.rstrip('/')
        self._user = user_name
        self._pass = password
        self._wrapped_jira_connection = None

        # Internal representation is simply name of project. We have a 1:many mapping of JiraConnection
        # to JiraProjects, and cannot have multiple projects with the same name on a single JIRA underlying object.
        self._cached_jira_projects = {}  # type: Dict[str, JiraProject]

        if connection_name == 'unknown':
            raise ConfigError(
                'Got JiraConnection constructor call with no connection_name. Cannot use this.'
            )

        # Create the JIRA connection, bailing if we have an error with auth
        try:
            if utils.unit_test:
                self._wrapped_jira_connection = TestWrappedJiraConnectionStub()
            else:
                self._wrapped_jira_connection = JIRA(server=self._url,
                                                     basic_auth=(self._user,
                                                                 self._pass))
        except JIRAError as je:
            if '401' in str(je.response):
                print(
                    'Received HTTP 401 response. Likely a mistyped local argus password. Try again.'
                )
            elif '404' in str(je.response):
                print('Recieved HTTP 404 response with url: {}'.format(je.url))
            else:
                print('Received HTTP error response. url: {} response: {}'.
                      format(je.url, je.response))
            print('Exiting due to failed Jira Connection attempt.')
            exit()
        if utils.unit_test:
            print(
                'DEBUG MODE. JiraConnection stubbed to locally generated names. Will not save config changes nor query.'
            )
        else:
            print('JIRA connection active for {}.'.format(
                self.connection_name))

        self.save_config()
Example #5
0
 def get_jira_connection(self, connection_name):
     if connection_name not in list(self._jira_connections.keys()):
         raise ConfigError(
             'Failed to find connection: {}'.format(connection_name))
     return self._jira_connections[connection_name]
Example #6
0
 def get_connection(self, connection_name):
     if connection_name not in self.connection_names:
         raise ConfigError(
             'Failed to get connection: {}'.format(connection_name))
     return self.jenkins_connections[connection_name]
Example #7
0
 def get_custom_report(self, report_name):
     if report_name not in list(self.jenkins_reports.keys()):
         raise ConfigError(
             'Failed to get custom report: {}'.format(report_name))
     return self.jenkins_reports[report_name]
Example #8
0
    def __init__(self, jira_connection: Optional['JiraConnection'],
                 issue: Issue, **kwargs: Dict) -> None:
        """
        :exception ConfigError: On deser, if we encounter an unknown conversion from an old version we raise ConfigError.
            All other values are blindly stored in the object, so we may run into corruption that transparently passes
            through here.
        """
        super(JiraIssue, self).__init__(**kwargs)
        if jira_connection:
            self.jira_connection_name = jira_connection.connection_name
        else:
            self.jira_connection_name = 'None'
        self.issue_key = issue.key
        self.dependencies = set()  # type: Set[JiraDependency]
        self.version = 1

        # bool indicates whether this is a fully functional JiraIssue or just a dummy placeholder w/issuekey for dep resolution
        self.is_cached_offline = True

        # We convert from the issue.fields dict object into local attributes of the JiraIssue rather than nesting
        # them inside a separate collection.
        # Protect against None on unit test stubs
        if issue.fields:
            for k, v in six.iteritems(issue.fields.__dict__):
                # Convert issuelinks to a local id:type dependency mapping. We store a list of strings as issuelinks.
                # Since we can have multiple relationships to a single ticket, we store a list and allow duplicate
                # issuekey entries.
                if k == 'issuelinks' and len(v) > 0:
                    result = ''
                    for member in v:
                        if hasattr(member, 'inwardIssue'):
                            relation_direction = 'inward'
                            related_key = member.inwardIssue
                        else:
                            relation_direction = 'outward'
                            related_key = member.outwardIssue
                        result += '{}:{}:{},'.format(related_key, member.type,
                                                     relation_direction)
                    dict.__setitem__(self, 'issuelinks', result)

                elif k == 'fixVersions' and len(v) > 0:
                    result = []

                    # Some backwards compat logic necessary here based on serialized version on disk.
                    # This can be stored as a jira.resources.Version object, a raw string comma delim with name=, or a
                    # comma delim string of just id's (latest format)
                    for version in v:
                        # If serialized in a jira.resources.Version object, we only care about fixver string
                        if type(version) == Version:
                            result.append(str(version.name))
                        # Otherwise we convert from an interim raw string format to a parsed array of version strings:
                        #     [<JIRA Version: name='1.1.2', id='12321445'>, <JIRA Version: name='1.2.0 beta 1', id='12319262'>]
                        elif isinstance(version, str) and 'name' in version:
                            result_match = re.search(
                                'name=\'([0-9A-Za-z_\.]+)\'', version)
                            if not result_match:
                                raise ConfigError(
                                    'WARNING! Discovered fixVersions string with unexpected format. Expected "name=([value])" and got: {}.'
                                    .format(version))
                            result.append(result_match.group(1))
                        # If it's a list, we assume it's the list of the versions we're interested in. May need to revisit
                        # this assumption later.
                        elif isinstance(version, list):
                            result.append(version)
                        else:
                            raise ConfigError(
                                'Received unexpected type in fixVersion: {} for ticket: {}'
                                .format(type(version), self.issue_key))
                    self['fixVersions'] = ','.join(result)
                else:
                    dict.__setitem__(self, str(k), str(v))
Example #9
0
 def link_jira_connection(self, jira_connection: 'JiraConnection') -> None:
     if jira_connection.url != self._url:
         raise ConfigError(
             'Attempted to link mismatched JiraConnection {} to JiraProject {}'
             .format(jira_connection, self))
     self.jira_connection = jira_connection
Example #10
0
    def pick_assignees(self, max_count: int) -> Optional[List[str]]:
        """
        The available user count can be untenable for caching offline so we rely on the REST API rather than caching.
        :param: max_count [int] value >= 1 of max # of users to return
        :return: list of selected assignees
        """

        result = []  # type: List[str]

        while True:
            issue_key = get_input(
                'Input ticket name (PROJ-#) to enumerate possible assignees:')

            if issue_key == 'q':
                return None
            elif '-' not in issue_key:
                print('Invalid issue key (missing -). Aborting.')
            else:
                break

        # case sensitive
        issue_key = issue_key.upper()

        msg = 'Enter a substring to search assignee names for (real name, not UserName), [q] to Quit:'
        try:
            while True:
                snippet = get_input(msg, lowered=False)
                if snippet == 'q':
                    return result
                url = '{}/rest/api/2/user/assignable/search?username={}&project={}&issueKey={}'.format(
                    self._url, snippet,
                    issue_key.split('-')[0], issue_key)
                print('Querying user matches...')
                response = requests.get(url,
                                        auth=HTTPBasicAuth(
                                            self._user, self._pass))
                if response.status_code == 404:
                    print(
                        'Got a 404 on url: {}. Likely a missing issue, but could be a bug. Try again.'
                        .format(url))
                    return None
                elif response.status_code != 200:
                    raise ConfigError(
                        'Failed to retrieve assignees for project: {} matching substring: {}. HTTP return code: {}. url: {}'
                        .format(self.connection_name, snippet,
                                response.status_code, url))

                matched = []
                for val in response.json():
                    matched.append(val['displayName'])
                if len(matched) != 0:
                    picked = pick_value(
                        header=
                        'Which of the following assignees matching substring: {}?'
                        .format(snippet),
                        options=matched,
                        allow_exit=True,
                        exit_text='Enter another substring',
                        sort=True,
                        silent=False)
                    if picked is not None:
                        print('Added {}'.format(picked))
                        msg = 'Enter another substring to search for, [q] to Quit:'
                        result.append(picked)
                        if len(result) == max_count:
                            return result
        except (JIRAError, ConfigError):
            print(
                'Received error attempting to query user from JIRA. Aborting.')
            traceback.print_exc()
            return None