"listener_url": "http://{}:{}/app/update/promote".format(LOCAL_SERVER, PORT)
    },
        {
        "name":
        "AppPublishedToReviewStream",
        "post_url":
        "/qrs/notification?name=app&changeType=update&propertyName=publishTime&xrfkey=abcdefg123456789",
        "listener_url":
        "http://{}:{}/app/publish/review".format(LOCAL_SERVER, PORT)
    }]
})

while True:
    log_id = str(uuid.uuid4())
    try:
        s, base_url = qrs.establish_requests_session("local")

        for notification in NOTIFICATIONS["notifications"]:
            # create the notification
            data = notification["listener_url"]
            LOGGER.info("%s\tData: %s", log_id, data)
            post_url = notification["post_url"]
            full_url = BASE_URL + post_url
            LOGGER.info("%s\tPost URL: %s", log_id, full_url)

            r = s.post(full_url, json=data)

            LOGGER.info("%s\tCreated\tStatus code: %s\tPost URL: %s\tResponse: %s",
                        log_id, r.status_code, full_url, r.json())

        qrs.close_requests_session(s)
예제 #2
0
def email_on_publish_to_review(app_id):
    """
    Fires emails off to the addresses associated
    with the streams designated as approval streams
    once an app is published to one of them
    """
    app_change_status = "app_published_gather_info"
    formatter = logging.Formatter("%(asctime)s\t%(name)s\t%(levelname)s\t" +
                                  "%(process)d\t%(thread)d\t" +
                                  app_change_status + "\t%(message)s")
    HANDLER.setFormatter(formatter)
    LOGGER.addHandler(HANDLER)
    log_id = str(uuid.uuid4())

    if EMAIL_ALERTS:
        s, base_url = qrs.establish_requests_session("local")
        LOGGER.info("%s\tRequesting app/full info on '%s'", log_id, app_id)
        app_full_status, app_full_response = qrs.app_full(s, base_url, app_id)
        qrs.close_requests_session(s)
        if app_full_status != 200:
            LOGGER.error(
                "%s\tSomething went wrong while trying to get app/full: %s",
                log_id,
                app_full_status,
            )
        else:
            LOGGER.debug("%s\tGot app/full data: %s", log_id, app_full_status)

        app_name = app_full_response["name"]
        LOGGER.info("%s\tApp Name: '%s'", log_id, app_name)

        app_owner = app_full_response["owner"]["userId"]
        app_owner_user_directory = app_full_response["owner"]["userDirectory"]
        app_owner = "{}\\{}".format(app_owner_user_directory, app_owner)
        LOGGER.info("%s\tApp Owner: '%s'", log_id, app_owner)
        modified_by_user = app_full_response["modifiedByUserName"]
        LOGGER.info("%s\tApp published by: '%s'", log_id, modified_by_user)

        stream_name = app_full_response["stream"]["name"]
        stream_id = app_full_response["stream"]["id"]
        LOGGER.info("%s\tPublished to stream Name: '%s'", log_id, stream_name)
        LOGGER.info("%s\tPublished to stream ID: '%s'", log_id, stream_id)

        LOGGER.info(
            "%s\tChecking to see if the stream '%s' has the custom property '%s'",
            log_id,
            stream_id,
            CUSTOM_PROPERTY_NAME_STREAM_ALERT_ON_PUBLISH,
        )

        s, base_url = qrs.establish_requests_session("local")
        LOGGER.info("%s\tRequesting stream/full info on '%s'", log_id,
                    stream_id)
        stream_full_status, stream_full_response = qrs.stream_full(
            s, base_url, stream_id)
        qrs.close_requests_session(s)
        if stream_full_status != 200:
            LOGGER.error(
                "%s\tSomething went wrong while trying to get stream/full: %s",
                log_id,
                stream_full_status,
            )
        else:
            LOGGER.debug("%s\tGot stream/full data: %s", log_id,
                         stream_full_status)

        stream_custom_properties = stream_full_response["customProperties"]
        LOGGER.debug("%s\tStream custom properties: %s", log_id,
                     stream_custom_properties)

        stream_marked_for_approval = False
        for stream_prop in stream_custom_properties:
            if (stream_prop["definition"]["name"] ==
                    CUSTOM_PROPERTY_NAME_STREAM_ALERT_ON_PUBLISH):
                stream_marked_for_approval = True
                break

        if stream_marked_for_approval:
            app_change_status = "app_published_to_approval_stream"
            formatter = logging.Formatter(
                "%(asctime)s\t%(name)s\t%(levelname)s\t" +
                "%(process)d\t%(thread)d\t" + app_change_status +
                "\t%(message)s")
            HANDLER.setFormatter(formatter)
            LOGGER.addHandler(HANDLER)
            LOGGER.info(
                "%s\tApp published to approval stream: '%s', configuring email",
                log_id,
                stream_name,
            )

            try:
                EMAIL_MAP_FILE_DIRECTORY = str(
                    Path(__file__).parent.parent).replace("\\", "/")
                EMAIL_MAP_FILE = (
                    EMAIL_MAP_FILE_DIRECTORY +
                    "/static/ApprovalStreamsToEmailAddressMap.csv")
                EMAIL_MAP_STREAM_LIST = []
                EMAIL_MAP_ADDRESS_LIST = []

                with open(EMAIL_MAP_FILE) as email_map:
                    reader = csv.reader(email_map, delimiter=",")
                    next(reader, None)  # skip the header
                    for row in reader:
                        EMAIL_MAP_STREAM_LIST.append(row[0])
                        EMAIL_MAP_ADDRESS_LIST.append([row[0], row[1]])

                recipient_list = []
                for stream_to_email in EMAIL_MAP_ADDRESS_LIST:
                    if stream_id == stream_to_email[0]:
                        recipient_list.append(stream_to_email[1].strip())

                recipient_list = list(set(recipient_list))
                LOGGER.info("%s\tRecipient list: '%s'", log_id, recipient_list)

                subject = "'{}' published to: '{}'".format(
                    app_name, stream_name)
                body = """Application: '{}'\nOwned by: '{}'\nPublished by: '{}'\nPublished to: '{}'
                       \n\nView the app here: https://{}/sense/app/{}""".format(
                    app_name,
                    app_owner,
                    modified_by_user,
                    stream_name,
                    LOCAL_SERVER_FQDN,
                    app_id,
                )
                message = """From: %s\nTo: %s\nSubject: %s\n\n%s""" % (
                    PROMOTION_SENDER_EMAIL,
                    ", ".join(recipient_list),
                    subject,
                    body,
                )

                try:
                    server = smtplib.SMTP(PROMOTION_SMTP, PROMOTION_SMTP_PORT)
                    server.ehlo()
                    server.starttls()
                    server.login(PROMOTION_SENDER_EMAIL,
                                 PROMOTION_SENDER_PASS_DECRYPTED)
                    server.sendmail(PROMOTION_SENDER_EMAIL, recipient_list,
                                    message)
                    server.close()
                    LOGGER.info("%s\tSuccessfully sent the email to %s",
                                log_id, recipient_list)
                except Exception as error:
                    LOGGER.error(
                        "%s\tThere was an error trying to send the email %s",
                        log_id,
                        error,
                    )

            except Exception as error:
                LOGGER.error(
                    "%s\tSomething went wrong with the email alerts: %s",
                    log_id, error)
                LOGGER.error(
                    "%s\tEnsure you have filled out 'ApprovalStreamsToEmailAddressMap.csv' properly",
                    log_id,
                )
        else:
            LOGGER.info(
                "%s\tStream '%s' with id '%s' does not contain the custom property '%s'. Exiting.",
                log_id,
                stream_name,
                stream_id,
                CUSTOM_PROPERTY_NAME_STREAM_ALERT_ON_PUBLISH,
            )

    return "Finished"
예제 #3
0
def email_promotion_results(
    app_name, user_id, modified_by_user, promotion_results, streams_not_found
):
    """
    Fires an email off to the app owner with the promotion results
    """
    log_id = str(uuid.uuid4())

    s, base_url = qrs.establish_requests_session("local")
    LOGGER.info(
        "%s\tRequesting user info on '%s' who owns '%s'", log_id, user_id, app_name
    )
    user_full_Status, user_full_response = qrs.user_full(s, base_url, user_id)
    qrs.close_requests_session(s)

    user_directory = user_full_response["userDirectory"]
    user_id = user_full_response["userId"]
    user = user_directory + "\\" + user_id
    LOGGER.info("%s\tUser: '******'", log_id, user)

    if user_full_Status != 200:
        LOGGER.error(
            "%s\tSomething went wrong while trying to get user/full: %s",
            log_id,
            user_full_Status,
        )
    else:
        LOGGER.debug("%s\tGot user/full data: %s", log_id, user_full_Status)

    user_email_found = False
    for attribute in user_full_response["attributes"]:
        if attribute["attributeType"].lower() == "email":
            user_email = attribute["attributeValue"]
            recipient_list = [user_email]
            LOGGER.info("%s\tUser email found: '%s'", log_id, user_email)
            user_email_found = True
            break

    if user_email_found:
        subject = "'{}' promotion results".format(app_name)
        body = "Application: '{}' has been promoted!\n\n".format(app_name)

        body += "Succesfully promoted the following apps:\n\n"
        for promotion in promotion_results:
            alias = promotion["remote_server_alias"]
            app_id = promotion["remote_app_id"]
            stream_name = promotion["remote_stream_name"]
            fqdn = promotion["remote_server_fqdn"]

            body += "Successfully deployed to server with alias: '{}'\n".format(alias)
            body += "Successfully deployed to server: '{}'\n".format(fqdn)
            body += "Successfully deployed to stream: '{}'\n".format(stream_name)
            body += "New app id: '{}'\n".format(app_id)
            application_url = "https://" + fqdn + "/sense/app/" + app_id
            body += "Application url: '{}'\n\n".format(application_url)

        if len(streams_not_found) >= 1:
            body += "\nStreams not found:\n\n"

            for promotion_failure in streams_not_found:
                stream_name = promotion_failure[0]
                alias = promotion_failure[1]["server_alias"]
                fqdn = promotion_failure[1]["server"]
                body += "Stream '{}' not found on server '{}' with alias '{}'\n".format(
                    stream_name, fqdn, alias
                )

        message = """From: %s\nTo: %s\nSubject: %s\n\n%s""" % (
            PROMOTION_SENDER_EMAIL,
            ", ".join(recipient_list),
            subject,
            body,
        )
        try:
            server = smtplib.SMTP(PROMOTION_SMTP, PROMOTION_SMTP_PORT)
            server.ehlo()
            server.starttls()
            server.login(PROMOTION_SENDER_EMAIL, PROMOTION_SENDER_PASS_DECRYPTED)
            server.sendmail(PROMOTION_SENDER_EMAIL, recipient_list, message)
            server.close()
            LOGGER.info("%s\tSuccessfully sent the email to %s", log_id, recipient_list)
            return "Successfully sent the email to: '{}'".format(recipient_list)
        except Exception as error:
            LOGGER.error(
                "%s\tThere was an error trying to send the email %s", log_id, error
            )
            return "There was an error trying to send the email: '{}'".format(error)
    else:
        LOGGER.warning("%s\tUser email not found. Exiting.", log_id)
        return "User email not found. No action taken."
예제 #4
0
def email_approval_status(app_name, user_id, modified_by_user, approved=True):
    '''
    Fires an email off whenever the custom property is changed on
    an app that is responsible for approving or denying an app's promotion
    '''
    log_id = str(uuid.uuid4())

    s, base_url = qrs.establish_requests_session("local")
    LOGGER.info("%s\tRequesting user info on '%s' who owns '%s'", log_id,
                user_id, app_name)
    user_full_Status, user_full_response = qrs.user_full(s, base_url, user_id)
    qrs.close_requests_session(s)

    owner_name = user_full_response["name"]

    if user_full_Status != 200:
        LOGGER.error(
            "%s\tSomething went wrong while trying to get user/full: %s",
            log_id, user_full_Status)
    else:
        LOGGER.debug("%s\tGot user/full data: %s", log_id, user_full_Status)

    user_email_found = False
    for attribute in user_full_response["attributes"]:
        if attribute["attributeType"].lower() == "email":
            user_email = attribute["attributeValue"]
            recipient_list = [user_email]
            LOGGER.info("%s\tUser email found: '%s'", log_id, user_email)
            user_email_found = True
            break

    if user_email_found:
        if approved:
            subject = "'{}' promotion approved by: '{}'".format(
                app_name, modified_by_user)
            body = "Application: '{}' owned by: '{}' approved by: '{}'".format(
                app_name, owner_name, modified_by_user)
            message = """From: %s\nTo: %s\nSubject: %s\n\n%s""" % (
                PROMOTION_SENDER_EMAIL, ", ".join(recipient_list), subject,
                body)
        else:
            subject = "'{}' promotion denied by: '{}'".format(
                app_name, modified_by_user)
            body = "Application: '{}' owned by: '{}' denied by: '{}'".format(
                app_name, owner_name, modified_by_user)
            message = """From: %s\nTo: %s\nSubject: %s\n\n%s""" % (
                PROMOTION_SENDER_EMAIL, ", ".join(recipient_list), subject,
                body)
        try:
            server = smtplib.SMTP(PROMOTION_SMTP, PROMOTION_SMTP_PORT)
            server.ehlo()
            server.starttls()
            server.login(PROMOTION_SENDER_EMAIL, PROMOTION_SENDER_PASS)
            server.sendmail(PROMOTION_SENDER_EMAIL, recipient_list, message)
            server.close()
            LOGGER.info("%s\tSuccessfully sent the email to %s", log_id,
                        recipient_list)
            return "Successfully sent the email to: '{}'".format(
                recipient_list)
        except Exception as error:
            LOGGER.error("%s\tThere was an error trying to send the email %s",
                         log_id, error)
            return "There was an error trying to send the email: '{}'".format(
                error)
    else:
        LOGGER.info("%s\tUser email not found. Exiting.")
        return "User email not found. No action taken."
예제 #5
0
def promote_app(app_id, originator_node_id, originator_host_name):
    '''
    Triggers every time an app is updated
    '''

    log_id = str(uuid.uuid4())
    LOGGER.info("%s\t_____________App Updated_____________", log_id)
    LOGGER.info("%s\tOriginator Node HostName: '%s'", log_id,
                originator_host_name)
    LOGGER.info("%s\tOriginator Node ID: '%s'", log_id, originator_node_id)

    s, base_url = qrs.establish_requests_session("local")
    LOGGER.info("%s\tRequesting app/full info on '%s'", log_id, app_id)
    app_full_status, app_full_response = qrs.app_full(s, base_url, app_id)
    qrs.close_requests_session(s)
    if app_full_status != 200:
        LOGGER.error(
            "%s\tSomething went wrong while trying to get app/full: %s",
            log_id, app_full_status)
    else:
        LOGGER.debug("%s\tGot app/full data: %s", log_id, app_full_status)

    app_name = app_full_response["name"]
    LOGGER.info("%s\tApp Name: '%s'", log_id, app_name)

    app_owner_id = app_full_response["owner"]["id"]
    app_owner = app_full_response["owner"]["userId"]
    app_owner_user_directory = app_full_response["owner"]["userDirectory"]
    app_owner = "{}\\{}".format(app_owner_user_directory, app_owner)
    modified_by_user = app_full_response["modifiedByUserName"]
    modified_date = app_full_response["modifiedDate"]

    # set the description that will be applied to promoted apps
    description = "App promoted from node: '{}' by: '{}' at: '{}' where it was owned by: '{}'".format(
        originator_host_name, modified_by_user, modified_date, app_owner)
    LOGGER.info(
        "%s\tApp updated on node: '%s' modified by: '%s' owned by: '%s'",
        log_id, originator_host_name, modified_by_user, app_owner)

    app_num_custom_properties = len(app_full_response["customProperties"])
    LOGGER.info("%s\tApp Number of Custom Properties: '%s'", log_id,
                app_num_custom_properties)

    # if the app is not published, no action will be taken
    LOGGER.info("%s\tChecking to see if app is published", log_id)

    app_is_published = app_full_response["published"]
    if app_is_published:
        LOGGER.info("%s\tApp is published", log_id)
    else:
        LOGGER.info("%s\tApp is not published.", log_id)

    if app_num_custom_properties >= 1 and app_is_published:
        promote_custom_prop_found = False
        promote_stream_custom_prop_found = False
        promotion_approved = False
        promotion_approval_empty = True
        promotion_approval_bad_input = False
        app_versioning_value_true = False
        unpublish_app_custom_prop_found = False
        unpublish_app_value_true = False

        LOGGER.info(
            "%s\tSearching app/full to see if '%s' and/or '%s' custom properties exist",
            log_id, CUSTOM_PROPERTY_NAME_PROMOTE,
            CUSTOM_PROPERTY_NAME_PROMOTE_STREAM)
        stream_value_list = []
        custom_property_name_promote_value_count = 0
        custom_prop_version_value_count = 0
        for custom_prop in app_full_response["customProperties"]:
            if custom_prop["definition"][
                    "name"] == CUSTOM_PROPERTY_NAME_PROMOTE:
                promote_value = custom_prop["value"]
                LOGGER.info(
                    "%s\tMandatory custom property '%s' exists with the value of: '%s'",
                    log_id, CUSTOM_PROPERTY_NAME_PROMOTE, promote_value)
                promote_custom_prop_found = True
                custom_property_name_promote_value_count += 1
            elif custom_prop["definition"][
                    "name"] == CUSTOM_PROPERTY_NAME_PROMOTE_STREAM:
                promote_stream_value = custom_prop["value"]
                LOGGER.info(
                    "%s\tMandatory custom property '%s' exists with the value of: '%s'",
                    log_id, CUSTOM_PROPERTY_NAME_PROMOTE_STREAM,
                    promote_stream_value)
                promote_stream_custom_prop_found = True
                stream_value_list.append(promote_stream_value)
            elif custom_prop["definition"][
                    "name"] == CUSTOM_PROPERTY_NAME_PROMOTE_APPROVAL:
                promotion_approval_empty = False
                promote_approval_value = custom_prop["value"]
                LOGGER.info(
                    "%s\tMandatory custom property '%s' exists with the value of: '%s'",
                    log_id, CUSTOM_PROPERTY_NAME_PROMOTE_APPROVAL,
                    promote_approval_value)
                if "approve" in promote_approval_value.lower():
                    LOGGER.info(
                        "%s\tApp versioning custom property '%s' with the value of: '%s'",
                        log_id, CUSTOM_PROPERTY_NAME_PROMOTE_APPROVAL,
                        promote_approval_value)
                    promotion_approved = True
                    LOGGER.info("%s\tPromotion has been approved by: '%s'",
                                log_id, modified_by_user)
                    if SEND_EMAIL_ON_APPROVAL_STATUS:
                        email_approval_status_response = mailer.email_approval_status(
                            app_name, app_owner_id, modified_by_user)
                        LOGGER.info("%s\tEmail response: '%s'", log_id,
                                    email_approval_status_response)
                elif "den" in promote_approval_value.lower():
                    LOGGER.info("%s\tPromotion has been denied by: '%s'",
                                log_id, modified_by_user)
                    if SEND_EMAIL_ON_APPROVAL_STATUS:
                        email_approval_status_response = mailer.email_approval_status(
                            app_name,
                            app_owner_id,
                            modified_by_user,
                            approved=False)
                        LOGGER.info("%s\tEmail response: '%s'", log_id,
                                    email_approval_status_response)
                else:
                    LOGGER.info(
                        "%s\tThis app will not be promoted as the approval value does not contain 'approve' or 'den'.",
                        log_id)
                    promotion_approval_bad_input = True
            elif custom_prop["definition"][
                    "name"] == UNPUBLISH_CUSTOM_PROP_NAME:
                unpublish_app_custom_prop_found = True
                custom_prop_unpublish_value = custom_prop["value"].lower()
                if AUTO_UNPUBLISH:
                    LOGGER.warning(
                        "%s\tThe custom property '%s' is found but the config file has the unpublish set to auto. This custom property will have no impact.",
                        log_id, UNPUBLISH_CUSTOM_PROP_NAME)
                else:
                    if "true" in custom_prop_unpublish_value:
                        LOGGER.info(
                            "%s\tThe custom property '%s' is found with the value 'True' (case insenstive). The app will be unpublished on successful approval or denial.",
                            log_id, UNPUBLISH_CUSTOM_PROP_NAME)
                        unpublish_app_value_true = True
                    else:
                        LOGGER.warning(
                            "%s\tThe custom property '%s' is found with the value '%s'. The value must be set to 'True' (case insensitive) for this custom property to be leveraged",
                            log_id, UNPUBLISH_CUSTOM_PROP_NAME,
                            custom_prop_unpublish_value)
            elif APP_VERSIONING and APP_VERSION_ON_CHANGE == "true":
                if custom_prop["definition"][
                        "name"] == VERSIONING_CUSTOM_PROP_NAME:
                    custom_prop_version_value_count += 1
                    versioning_custom_prop_value = custom_prop["value"].lower()
                    if versioning_custom_prop_value == "true":
                        LOGGER.info(
                            "%s\tApp versioning custom property '%s' with the value of: '%s'",
                            log_id, VERSIONING_CUSTOM_PROP_NAME,
                            versioning_custom_prop_value)
                        app_versioning_value_true = True
                    elif custom_prop_version_value_count == 1:
                        LOGGER.warning(
                            "%s\tThis app will not be versioned as the value must be set to 'True' (case insensitive)",
                            log_id)
            elif custom_prop["definition"][
                    "name"] == VERSIONING_CUSTOM_PROP_NAME:
                LOGGER.info(
                    "%s\tVersioning is not enabled though the custom property '%s' is found. No action taken.",
                    log_id, VERSIONING_CUSTOM_PROP_NAME)

        if not AUTO_UNPUBLISH and not unpublish_app_custom_prop_found:
            LOGGER.warning(
                "%s\tAuto unpublish is not enabled and the custom property '%s' is not found. The app will not be unpublished.",
                log_id, UNPUBLISH_CUSTOM_PROP_NAME)

        if (promotion_approved and promote_custom_prop_found
                and promote_stream_custom_prop_found
                and custom_property_name_promote_value_count == 1):

            LOGGER.info(
                "%s\tMandatory custom properties have values, proceeding",
                log_id)
            # lookup all of the streams by name to see if they are valid
            stream_id_list = []
            matching_stream_list = []
            final_id_list = []
            i = -1
            s, base_url = qrs.establish_requests_session("remote")
            LOGGER.info(
                "%s\tGetting Stream IDs from remote server by name for streams: '%s'",
                log_id, stream_value_list)
            for stream in stream_value_list:
                i += 1

                stream_id_status, stream_id = qrs.get_remote_stream_id_by_name(
                    s, base_url, stream)
                if stream_id_status != 200:
                    LOGGER.error(
                        "%s\tSomething went wrong while trying to get the ID for stream: '%s'",
                        log_id, stream)
                    LOGGER.error("%s\tStatus: '%s'", log_id, stream_id_status)
                    LOGGER.debug("%s\tGet stream ID call status: '%s'", log_id,
                                 stream_id_status)
                if stream_id != None:
                    matching_stream_list.append(stream_value_list[i])
                    stream_id_list.append(stream_id)
                    LOGGER.info("%s\tStream found: '%s'", log_id,
                                stream_value_list[i])
                else:
                    LOGGER.warning("%s\tStream not found: '%s'", log_id,
                                   stream_value_list[i])

            qrs.close_requests_session(s)
            LOGGER.info("%s\tStream ID List: '%s'", log_id, stream_id_list)
            stream_existing_count = len(matching_stream_list)

            if stream_existing_count >= 1 and (
                    "overwrite" in promote_value.lower()
                    or "duplicate" in promote_value.lower()):

                s, base_url = qrs.establish_requests_session("local")
                LOGGER.info("%s\tExporting local app", log_id)
                export_app_status = qrs.export_app(s, base_url, app_id,
                                                   app_name)
                qrs.close_requests_session(s)
                if export_app_status != 200:
                    LOGGER.error(
                        "%s\tSomething went wrong while trying to export the app: '%s'",
                        log_id, export_app_status)
                else:
                    LOGGER.debug("%s\tApp exported: '%s'", log_id,
                                 export_app_status)

                if "overwrite" in promote_value.lower(
                ) and stream_existing_count >= 1:
                    LOGGER.info(
                        "%s\tApps that exist with the same name in target streams will be overwritten. If they do not exist, they will be created.",
                        log_id)
                    s, base_url = qrs.establish_requests_session("remote")
                    LOGGER.info(
                        "%s\tLooking up app IDs to be overwritten on the remote server by name",
                        log_id)
                    remote_app_id_status, remote_app_id_json = qrs.get_remote_app_ids_by_name(
                        s, base_url, app_name)
                    num_remote_app_ids = len(remote_app_id_json)

                    if remote_app_id_status != 200:
                        LOGGER.error(
                            "%s\tSomething went wrong when looking up apps by name: '%s'",
                            log_id, remote_app_id_status)
                    else:
                        LOGGER.debug("%s\tCall to look up apps status: '%s'",
                                     log_id, remote_app_id_status)

                    LOGGER.info("%s\tApp IDs found with matching names: '%s'",
                                log_id, num_remote_app_ids)

                    remote_app_detail_list = []
                    matching_app_ids = []
                    matching_published_app_ids = []
                    num_remote_found_target_published = 0
                    if num_remote_app_ids >= 1:
                        for app in remote_app_id_json:
                            remote_app_id = app["id"]
                            matching_app_ids.append(remote_app_id)
                            remote_app_published = app["published"]
                            if remote_app_published:
                                remote_stream_id = app["stream"]["id"]
                                remote_stream_name = app["stream"]["name"]

                                for sid in stream_id_list:
                                    if sid == remote_stream_id:
                                        matching_published_app_ids.append(
                                            remote_app_id)
                                        remote_app_detail_list.append({
                                            "app_id":
                                            remote_app_id,
                                            "published":
                                            remote_app_published,
                                            "stream_id":
                                            remote_stream_id,
                                            "stream_name":
                                            remote_stream_name
                                        })

                        qrs.close_requests_session(s)
                        num_remote_found_target_published = len(
                            remote_app_detail_list)

                        LOGGER.info("%s\tMatching App IDs: '%s'", log_id,
                                    matching_app_ids)
                        LOGGER.info(
                            "%s\tMatching apps with matching names that are published to target streams: '%s'",
                            log_id, matching_published_app_ids)
                        LOGGER.debug("%s\tApp info: '%s'", log_id,
                                     remote_app_detail_list)

                    left_over_stream_id_list = stream_id_list
                    if num_remote_found_target_published >= 1:
                        s, base_url = qrs.establish_requests_session("remote")
                        LOGGER.info(
                            "%s\tUploading app onto remote server and getting the new ID",
                            log_id)
                        upload_app_status, new_app_id = qrs.upload_app(
                            s, base_url, app_name)
                        if upload_app_status != 201:
                            LOGGER.error(
                                "%s\tSomething went wrong while trying to upload the app: '%s'",
                                log_id, upload_app_status)
                        else:
                            LOGGER.debug("%s\tApp uploaded: '%s'", log_id,
                                         upload_app_status)
                        qrs.close_requests_session(s)

                        s, base_url = qrs.establish_requests_session("remote")
                        LOGGER.info(
                            "%s\tOverwriting existing apps with matching names published to target streams",
                            log_id)
                        for remote_app_id in remote_app_detail_list:
                            app_replaced_status = qrs.app_replace(
                                s, base_url, new_app_id,
                                remote_app_id["app_id"])
                            qrs.close_requests_session(s)
                            if app_replaced_status != 200:
                                LOGGER.error(
                                    "%s\tSomething went wrong while trying to replace the app(s): '%s'",
                                    log_id, app_replaced_status)
                            else:
                                LOGGER.info(
                                    "%s\tSuccessfully replaced app: '%s' in the stream: '%s'",
                                    log_id, remote_app_id["app_id"],
                                    remote_app_id["stream_name"])
                                app_promoted = True
                                final_id_list.append(remote_app_id["app_id"])
                            try:
                                pop_this = remote_app_id["stream_id"]
                                left_over_stream_id_list.remove(pop_this)
                            except:
                                pass
                        qrs.close_requests_session(s)

                        LOGGER.info(
                            "%s\tDeleting application that was used to overwrite",
                            log_id)
                        s, base_url = qrs.establish_requests_session("remote")
                        qrs.app_delete(s, base_url, new_app_id)
                        if app_replaced_status != 200:
                            LOGGER.error(
                                "%s\tSomething went wrong while trying to delete the app: '%s'",
                                log_id, app_replaced_status)
                        else:
                            LOGGER.debug(
                                "%s\tSuccessfully deleted the app: '%s'",
                                log_id, app_replaced_status)
                        qrs.close_requests_session(s)

                    if len(left_over_stream_id_list) >= 1:
                        LOGGER.info(
                            "%s\tUploading and publishing apps to existing streams that did not contain any matching app names",
                            log_id)
                        i = -1
                        for stream_id in left_over_stream_id_list:
                            i += 1
                            if stream_id != None:
                                s, base_url = qrs.establish_requests_session(
                                    "remote")
                                LOGGER.info(
                                    "%s\tUploading app onto remote server and getting the new ID",
                                    log_id)
                                upload_app_status, new_app_id = qrs.upload_app(
                                    s, base_url, app_name)
                                if upload_app_status != 201:
                                    LOGGER.error(
                                        "%s\tSomething went wrong while trying to upload the app: '%s'",
                                        log_id, upload_app_status)
                                else:
                                    LOGGER.debug("%s\tApp uploaded: '%s'",
                                                 log_id, upload_app_status)
                                qrs.close_requests_session(s)

                                s, base_url = qrs.establish_requests_session(
                                    "remote")
                                LOGGER.info(
                                    "%s\tPublishing app '%s' to stream '%s'",
                                    log_id, new_app_id, stream_id)
                                app_published_status = qrs.publish_to_stream(
                                    s, base_url, new_app_id, stream_id)
                                qrs.close_requests_session(s)
                                if app_published_status != 200:
                                    LOGGER.error(
                                        "%s\tSomething went wrong while trying to publish the app to: '%s', '%s'",
                                        log_id, stream_id,
                                        app_published_status)
                                else:
                                    LOGGER.debug(
                                        "%s\tApp published status: '%s'",
                                        log_id, app_published_status)
                                    LOGGER.info(
                                        "%s\tSuccessfully published app '%s' to stream '%s'",
                                        log_id, new_app_id, stream_id)
                                    app_promoted = True
                                    final_id_list.append(new_app_id)
                            else:
                                pass

                # the app will not overwrite an app unless the target stream
                # exists
                elif "overwrite" in promote_value.lower():
                    LOGGER.info(
                        "%s\tApp is set to overwrite, but no target streams exist. Exiting.",
                        log_id)

                # if the app is set to duplicate and if any target streams exist on the server, new apps will be uploaded and published
                # to them, regardless if any apps previously existed or not
                elif "duplicate" in promote_value.lower(
                ) and stream_existing_count >= 1:
                    LOGGER.info(
                        "%s\tNew copies of the application will be published to the target streams if they exist",
                        log_id)
                    i = -1
                    for stream_id in stream_id_list:
                        i += 1
                        if stream_id != None:
                            s, base_url = qrs.establish_requests_session(
                                "remote")
                            LOGGER.info(
                                "%s\tUploading app onto remote server and getting the new ID",
                                log_id)
                            upload_app_status, new_app_id = qrs.upload_app(
                                s, base_url, app_name)
                            if upload_app_status != 201:
                                LOGGER.error(
                                    "%s\tSomething went wrong while trying to upload the app: '%s'",
                                    log_id, upload_app_status)
                            else:
                                LOGGER.debug("%s\tApp uploaded: '%s'", log_id,
                                             upload_app_status)
                            qrs.close_requests_session(s)

                            s, base_url = qrs.establish_requests_session(
                                "remote")
                            LOGGER.info("%s\tPublishing app to: '%s'", log_id,
                                        stream_id)
                            app_published_status = qrs.publish_to_stream(
                                s, base_url, new_app_id, stream_id)
                            qrs.close_requests_session(s)
                            if app_published_status != 200:
                                LOGGER.error(
                                    "%s\tSomething went wrong while trying to publish: '%s'",
                                    log_id, app_published_status)
                            else:
                                LOGGER.debug("%s\tApp published status: '%s'",
                                             log_id, app_published_status)
                                LOGGER.info(
                                    "%s\tSuccessfully published app '%s' to stream '%s'",
                                    log_id, new_app_id, stream_id)
                                app_promoted = True
                                final_id_list.append(new_app_id)
                        else:
                            LOGGER.debug("%s\tCould not find stream: '%s'",
                                         log_id, stream_id)

                elif "duplicate" in promote_value.lower():
                    LOGGER.info(
                        "%s\tApp set to duplicate, but no target streams exist. Exiting.",
                        log_id)
                else:
                    LOGGER.info("%s\tSomething went wrong. Exiting.", log_id)

                # if the app successfully published any apps, it will consider
                # it a success
                if app_promoted:
                    # check if versioning is enabled
                    # if so, push to s3
                    LOGGER.info("%s\tApp promoted from:'%s' to '%s'", log_id,
                                originator_host_name, REMOTE_SERVER)
                    if APP_VERSIONING and app_versioning_value_true:
                        LOGGER.info("%s\tVersioning the app", log_id)
                        app_name_no_data = app_name + "-Template"

                        s, base_url = qrs.establish_requests_session("local")
                        LOGGER.info(
                            "%s\tExporting local app without data for versioning",
                            log_id)
                        export_app_status = qrs.export_app(s,
                                                           base_url,
                                                           app_id,
                                                           app_name_no_data,
                                                           skip_data=True)
                        qrs.close_requests_session(s)
                        if export_app_status != 200:
                            LOGGER.error(
                                "%s\tSomething went wrong while trying to export the app without data: '%s'",
                                log_id, export_app_status)
                        else:
                            LOGGER.debug("%s\tApp exported without data: '%s'",
                                         log_id, export_app_status)

                        LOGGER.info("%s\tAttempting to connect s3", log_id)
                        app_file_name = app_name_no_data + ".qvf"
                        app_abs_path = EXPORTED_APP_DIRECTORY + app_file_name
                        key = S3_PREFIX + app_file_name
                        try:
                            s3 = boto3.client("s3")
                            transfer = S3Transfer(s3)
                            LOGGER.info("%s\tConnected to s3", log_id)
                            try:
                                LOGGER.info(
                                    "%s\tTrying to upload the app '%s' to the bucket '%s' with prefix '%s'",
                                    log_id, app_file_name, S3_BUCKET,
                                    S3_PREFIX)
                                transfer.upload_file(app_abs_path, S3_BUCKET,
                                                     key)
                                LOGGER.info(
                                    "%s\tApp uploaded successfully to '%s'",
                                    log_id, S3_BUCKET)
                                try:
                                    LOGGER.info(
                                        "%s\tGetting the version id of the s3 object",
                                        log_id)
                                    s3 = boto3.resource("s3")
                                    app_s3_version_id = str(
                                        s3.Object(S3_BUCKET, key).version_id)
                                    description += "\n\nS3 Version ID: " + app_s3_version_id
                                    LOGGER.info("%s\tApp s3 version id: '%s'",
                                                log_id, app_s3_version_id)
                                    template_app_deleted_status = qrs.delete_local_app_export(
                                        app_name_no_data)
                                    if not template_app_deleted_status:
                                        LOGGER.error(
                                            "%s\tSomething went wrong while trying to delete the template app: '%s'",
                                            log_id,
                                            template_app_deleted_status)
                                    else:
                                        LOGGER.debug(
                                            "%s\tLocal app deleted: '%s'",
                                            log_id,
                                            template_app_deleted_status)
                                except Exception as error:
                                    LOGGER.error(
                                        "%s\tSomething went wrong while getting the version id from s3: '%s'",
                                        log_id, error)
                            except Exception as error:
                                LOGGER.error(
                                    "%s\tCould not upload the app: '%s'",
                                    log_id, error)
                        except Exception as error:
                            LOGGER.error("%s\tCould not connect to s3: '%s'",
                                         log_id, error)
                            LOGGER.error(
                                "%s\tPlease ensure that your server has programmatic access such as an IAM role to the bucket enabled",
                                log_id)

                    # update the description for each of the published apps
                    s, base_url = qrs.establish_requests_session("remote")
                    LOGGER.info(
                        "%s\tAdding a description to the remote app(s)",
                        log_id)
                    for published_app_id in final_id_list:
                        # add a description to the remote app that
                        # states who promoted it and when
                        description_status_code = qrs.modify_app_description(
                            s, base_url, published_app_id, description)
                        if description_status_code != 200:
                            LOGGER.error(
                                "%s\tSomething went wrong while trying to add a description to the app: '%s'",
                                log_id, description_status_code)
                        else:
                            LOGGER.info(
                                "%s\tDescription successfully added to the app: '%s', '%s'",
                                log_id, description_status_code,
                                published_app_id)
                    qrs.close_requests_session(s)

                    if AUTO_UNPUBLISH or unpublish_app_value_true:
                        LOGGER.info(
                            "%s\tDuplicating the local application: '%s'",
                            log_id, app_name)
                        s, base_url = qrs.establish_requests_session("local")
                        duplicated_app_status_code, duplicate_app_id = qrs.duplicate_app(
                            s, base_url, app_id, app_name)
                        qrs.close_requests_session(s)
                        if duplicated_app_status_code != 201:
                            LOGGER.error(
                                "%s\tSomething went wrong while duplicating the app: '%s'",
                                log_id, duplicated_app_status_code)
                        else:
                            LOGGER.info(
                                "%s\tSuccessfully duplicated app: '%s'",
                                log_id, app_name)

                        LOGGER.info(
                            "%s\tChanging the owner of the duplicated app back to '%s'",
                            log_id, app_owner)
                        s, base_url = qrs.establish_requests_session("local")
                        change_app_owner_status = qrs.change_app_owner(
                            s, base_url, duplicate_app_id, app_owner_id)
                        qrs.close_requests_session(s)

                        if change_app_owner_status != 200:
                            LOGGER.error(
                                "%s\tSomething went wrong while trying to change the app owner: '%s'",
                                log_id, change_app_owner_status)
                        else:
                            LOGGER.info(
                                "%s\tSuccessfully changed the app owner to: '%s'",
                                log_id, app_owner)

                        LOGGER.info("%s\tDeleting the published app", log_id)
                        s, base_url = qrs.establish_requests_session("local")
                        app_delete_status = qrs.app_delete(s, base_url, app_id)
                        qrs.close_requests_session(s)

                        if app_delete_status != 204:
                            LOGGER.error(
                                "%s\tSomething went wrong while trying to delete the app with id: '%s'",
                                log_id, app_id)
                        else:
                            LOGGER.info(
                                "%s\tSuccessfully deleted the app with id: '%s'",
                                log_id, app_id)
                    else:
                        LOGGER.info(
                            "%s\tRemoving promotion related custom properties if they exist from the published app including: '%s'",
                            log_id, PROP_LIST)

                        s, base_url = qrs.establish_requests_session("local")
                        removed_props_status = qrs.remove_props_from_app(
                            s, base_url, app_id, PROP_LIST)
                        qrs.close_requests_session(s)

                        if removed_props_status != 200:
                            LOGGER.error(
                                "%s\tSomething went wrong while trying to remove custom properties from the app: '%s'",
                                log_id, removed_props_status)
                        else:
                            LOGGER.info(
                                "%s\tSuccessfully removed promotion related custom properties from the app app: '%s'",
                                log_id, app_name)

                    # delete the local copy of the exported app
                    local_app_deleted_status = qrs.delete_local_app_export(
                        app_name)
                    if not local_app_deleted_status:
                        LOGGER.error(
                            "%s\tSomething went wrong while trying to delete the app: '%s'",
                            log_id, local_app_deleted_status)
                    else:
                        LOGGER.info("%s\tLocal app deleted: '%s'", log_id,
                                    local_app_deleted_status)

            else:
                LOGGER.warning(
                    "%s\tNo matching streams exist on the server. Exiting.",
                    log_id)

        elif (not promotion_approved and not promotion_approval_empty
              and not promotion_approval_bad_input
              and (AUTO_UNPUBLISH or unpublish_app_value_true)):

            LOGGER.info("%s\tApplication approval for '%s' has been denied",
                        log_id, app_name)
            LOGGER.info("%s\tDuplicating the local application: '%s'", log_id,
                        app_name)
            s, base_url = qrs.establish_requests_session("local")
            duplicated_app_status_code, duplicate_app_id = qrs.duplicate_app(
                s, base_url, app_id, app_name)
            qrs.close_requests_session(s)
            if duplicated_app_status_code != 201:
                LOGGER.error(
                    "%s\tSomething went wrong while duplicating the app: '%s'",
                    log_id, duplicated_app_status_code)
            else:
                LOGGER.info("%s\tSuccessfully duplicated app: '%s'", log_id,
                            app_name)

            LOGGER.info(
                "%s\tChanging the owner of the duplicated app back to '%s'",
                log_id, app_owner)
            s, base_url = qrs.establish_requests_session("local")
            change_app_owner_status = qrs.change_app_owner(
                s, base_url, duplicate_app_id, app_owner_id)
            qrs.close_requests_session(s)

            if change_app_owner_status != 200:
                LOGGER.error(
                    "%s\tSomething went wrong while trying to change the app owner: '%s'",
                    log_id, change_app_owner_status)
            else:
                LOGGER.info("%s\tSuccessfully changed the app owner to: '%s'",
                            log_id, app_owner)

            LOGGER.info("%s\tDeleting the published app", log_id)
            s, base_url = qrs.establish_requests_session("local")
            app_delete_status = qrs.app_delete(s, base_url, app_id)
            qrs.close_requests_session(s)

            if app_delete_status != 204:
                LOGGER.error(
                    "%s\tSomething went wrong while trying to delete the app with id: '%s'",
                    log_id, app_id)
            else:
                LOGGER.info("%s\tSuccessfully deleted the app with id: '%s'",
                            log_id, app_id)

        elif (not promotion_approved and not promotion_approval_empty
              and not promotion_approval_bad_input and not AUTO_UNPUBLISH):

            LOGGER.info(
                "%s\tRemoving promotion related custom properties if they exist from the published app including: '%s'",
                log_id, PROP_LIST)

            s, base_url = qrs.establish_requests_session("local")
            removed_props_status = qrs.remove_props_from_app(
                s, base_url, app_id, PROP_LIST)
            qrs.close_requests_session(s)

            if removed_props_status != 200:
                LOGGER.error(
                    "%s\tSomething went wrong while trying to remove custom properties from the app: '%s'",
                    log_id, removed_props_status)
            else:
                LOGGER.info(
                    "%s\tSuccessfully removed promotion related custom properties from the app app: '%s'",
                    log_id, app_name)

        elif custom_property_name_promote_value_count > 1:
            LOGGER.error(
                "%s\tThere can only be a single value in the custom property: '%s'. Exiting.",
                log_id, CUSTOM_PROPERTY_NAME_PROMOTE)
        elif promote_custom_prop_found and not promote_stream_custom_prop_found:
            LOGGER.info(
                "%s\tCustom property '%s' could not be found. Exiting.",
                log_id, CUSTOM_PROPERTY_NAME_PROMOTE_STREAM)
        elif promote_stream_custom_prop_found and not promote_custom_prop_found:
            LOGGER.info(
                "%s\tCustom property '%s' could not be found. Exiting.",
                log_id, CUSTOM_PROPERTY_NAME_PROMOTE)
        elif not promote_custom_prop_found and not promote_stream_custom_prop_found:
            LOGGER.info(
                "%s\tNeither the '%s' or the '%s' could be found. Exiting.",
                log_id, CUSTOM_PROPERTY_NAME_PROMOTE,
                CUSTOM_PROPERTY_NAME_PROMOTE_STREAM)
        elif promotion_approval_empty:
            LOGGER.info("%s\tThere is no mandatory value for '%s'. Exiting.",
                        log_id, CUSTOM_PROPERTY_NAME_PROMOTE_APPROVAL)
    if app_num_custom_properties == 0:
        LOGGER.info("%s\tNo custom properties could be found. Exiting.",
                    log_id)

    return "Finished"