static ADUC_Result StepsHandler_IsInstalled()

in src/extensions/update_manifest_handlers/steps_handler/src/steps_handler.cpp [1260:1468]


static ADUC_Result StepsHandler_IsInstalled(const tagADUC_WorkflowData* workflowData)
{
    ADUC_Result result{ ADUC_Result_Failure };

    ADUC_WorkflowHandle handle = workflowData->WorkflowHandle;
    ADUC_WorkflowHandle stepHandle = nullptr;

    char* workFolder = workflow_get_workfolder(handle);
    JSON_Array* selectedComponentsArray = nullptr;
    int workflowLevel = workflow_get_level(handle);
    int workflowStep = workflow_get_step_index(handle);
    int selectedComponentsCount = 0;
    char* serializedComponentString = nullptr;
    bool isComponentsEnumeratorRegistered = ExtensionManager::IsComponentsEnumeratorRegistered();

    Log_Debug("Evaluating is-installed state of the workflow (level %d, step %d).", workflowLevel, workflowStep);

    int createResult = ADUC_SystemUtils_MkSandboxDirRecursive(workFolder);
    if (createResult != 0)
    {
        Log_Error("Unable to create folder %s, error %d", workFolder, createResult);
        result = { ADUC_Result_Failure, ADUC_ERC_STEPS_HANDLER_CREATE_SANDBOX_FAILURE };
        goto done;
    }

    result = PrepareStepsWorkflowDataObject(handle);
    if (IsAducResultCodeFailure(result.ResultCode))
    {
        workflow_set_result_details(handle, "Invalid steps workflow collection");
        goto done;
    }

    if (workflowLevel == 0 || !isComponentsEnumeratorRegistered)
    {
        // To top-level step, or if component enumerator is not registered, we will assume that this reference update is
        // interned for the host device and will set selectedComponentCount to 1 to iterate through
        // every step once.
        selectedComponentsCount = 1;
    }
    else
    {
        // This is a reference step (workflowLevel == 1), this intended for one or more components.
        result = GetSelectedComponentsArray(handle, &selectedComponentsArray);
        if (IsAducResultCodeFailure(result.ResultCode))
        {
            const char* fmt = "Missing selected components. workflow level %d, step %d";
            Log_Error(fmt, workflowLevel, workflowStep);
            workflow_set_result_details(handle, fmt, workflowLevel, workflowStep);
            goto done;
        }

        selectedComponentsCount = static_cast<int>(json_array_get_count(selectedComponentsArray));

        if (selectedComponentsCount == 0)
        {
            // If there's no matching component, we will consider this step 'optional' and this
            // step is no-op. Returning 'installed' to skip this step.
            const char* msg = "Optional step (no matching components)";
            Log_Debug(msg);
            result = { ADUC_Result_IsInstalled_Installed };

            // This is a good opportunity to set the workflow state to indicates that
            // the current component is 'optional'.
            // We do this by setting workflow result code to ADUC_Result_Download_Skipped_NoMatchingComponents.
            ADUC_Result currentResult = workflow_get_result(handle);
            if (IsAducResultCodeFailure(currentResult.ResultCode))
            {
                ADUC_Result newResult = { ADUC_Result_Download_Skipped_NoMatchingComponents };
                workflow_set_result(handle, newResult);
                workflow_set_result_details(handle, msg);
            }

            goto done;
        }
    }

    // For each selected component, check whether the update has been installed.
    for (size_t iCom = 0, stepsCount = workflow_get_children_count(handle); iCom < selectedComponentsCount; iCom++)
    {
        serializedComponentString = CreateComponentSerializedString(selectedComponentsArray, iCom);

        // For each step (child workflow), invoke IsInstalled().
        for (size_t i = 0; i < stepsCount; i++)
        {
            if (IsStepsHandlerExtraDebugLogsEnabled())
            {
                Log_Debug(
                    "Evaluating child step #%d on component #%d.\n#### Component ####\n%s\n###################\n",
                    i,
                    iCom,
                    serializedComponentString);
            }

            // Use a wrapper workflow to hold a stepHandle.
            ADUC_WorkflowData stepWorkflow = {};

            stepHandle = workflow_get_child(handle, i);
            if (stepHandle == nullptr)
            {
                const char* errorFmt = "Cannot process child step #%lu due to missing (child) workflow data.";
                Log_Error(errorFmt, i);
                result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_ISINSTALLED_FAILURE_MISSING_CHILD_WORKFLOW;
                workflow_set_result_details(handle, errorFmt, i);
                goto done;
            }
            stepWorkflow.WorkflowHandle = stepHandle;

            // For inline step - set current component info on the workflow.
            if (serializedComponentString != nullptr && workflow_is_inline_step(handle, i))
            {
                if (!workflow_set_selected_components(stepHandle, serializedComponentString))
                {
                    result.ResultCode = ADUC_Result_Failure;
                    result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_SET_SELECTED_COMPONENTS_FAILURE;
                    workflow_set_result_details(handle, "Cannot set target component(s) for child step #%lu", i);
                    goto done;
                }
            }

            ContentHandler* contentHandler = nullptr;
            const char* stepUpdateType = workflow_is_inline_step(handle, i)
                ? workflow_peek_update_manifest_step_handler(handle, i)
                : DEFAULT_REF_STEP_HANDLER;

            Log_Debug("Loading handler for child step #%lu (handler: '%s')", i, stepUpdateType);

            result = ExtensionManager::LoadUpdateContentHandlerExtension(stepUpdateType, &contentHandler);

            if (IsAducResultCodeFailure(result.ResultCode))
            {
                const char* errorFmt = "Cannot load a handler for child step #%lu (handler :%s)";
                Log_Error(errorFmt, i, stepUpdateType);
                workflow_set_result_details(handle, errorFmt, i, stepUpdateType == nullptr ? "NULL" : stepUpdateType);
                goto done;
            }

            // If this step is already installed, skip to the next one.
            try
            {
                result = contentHandler->IsInstalled(&stepWorkflow);

                // If step is already installed, we should make sure that the current step's result is set correctly.
                if (result.ResultCode == ADUC_Result_IsInstalled_Installed)
                {
                    // Note: the step's workflow result will be reported to the IoT Hub when the workflow is finished.
                    // If the step is 'Installed', its workflow result should not be 'Failure'.
                    // We're setting the result code to ADUC_Result_Install_Skipped_UpdateAlreadyInstalled here
                    // to avoid potential confusion when customer viewing the twin data.
                    ADUC_Result stepWorkflowResult = workflow_get_result(stepWorkflow.WorkflowHandle);
                    if (stepWorkflowResult.ResultCode == ADUC_Result_Failure
                        || stepWorkflowResult.ResultCode == ADUC_Result_Failure_Cancelled)
                    {
                        ADUC_Result stepResultCode;
                        stepResultCode.ResultCode = ADUC_Result_Install_Skipped_UpdateAlreadyInstalled;
                        stepResultCode.ExtendedResultCode = 0;

                        workflow_set_result(stepWorkflow.WorkflowHandle, stepResultCode);
                    }
                }
            }
            catch (...)
            {
                // Cannot determine whether the step is installed, so, let's assume that it's not install.
                result.ResultCode = ADUC_Result_IsInstalled_NotInstalled;
                result.ExtendedResultCode = 0;
            }

            if (IsAducResultCodeFailure(result.ResultCode)
                || result.ResultCode == ADUC_Result_IsInstalled_NotInstalled)
            {
                Log_Info(
                    "Workflow lvl %d, step #%d, child step #%lu, component #%d is not installed.",
                    workflowLevel,
                    workflowStep,
                    i,
                    iCom);
                // We can stop here if we found one component that not installed.
                goto done;
            }

        } // steps
    } // components

    result.ResultCode = ADUC_Result_IsInstalled_Installed;
    result.ExtendedResultCode = 0;

    {
        // This is a good opportunity to set the workflow state to indicates that
        // the current component is up-to-date with its goal state.
        // We do this by setting workflow result code to ADUC_Result_Apply_Success.
        ADUC_Result currentResult = workflow_get_result(handle);
        if (IsAducResultCodeFailure(currentResult.ResultCode))
        {
            ADUC_Result newResult;
            newResult.ResultCode = ADUC_Result_Apply_Success;
            newResult.ExtendedResultCode = 0;
            workflow_set_result(handle, newResult);
        }
    }

done:

    json_free_serialized_string(serializedComponentString);
    workflow_free_string(workFolder);

    Log_Debug("Workflow lvl %d step #%d is-installed state %d", workflowLevel, workflowStep, result.ResultCode);

    return result;
}