ADUC_Result PrepareStepsWorkflowDataObject()

in src/extensions/update_manifest_handlers/steps_handler/src/steps_handler.cpp [80:273]


ADUC_Result PrepareStepsWorkflowDataObject(ADUC_WorkflowHandle handle)
{
    ADUC_Result result;
    result.ResultCode = ADUC_Result_Failure;
    result.ExtendedResultCode = 0;
    ADUC_WorkflowHandle childHandle = nullptr;

    auto stepCount = static_cast<size_t>(workflow_get_instructions_steps_count(handle));
    char* workFolder = workflow_get_workfolder(handle);
    size_t childWorkflowCount = workflow_get_children_count(handle);
    int workflowLevel = workflow_get_level(handle);

    // Child workflow should be either 0 (resuming install phase after agent restarted),
    // or equal to fileCount (already created children during download phase)
    if (childWorkflowCount != stepCount)
    {
        // Remove existing child workflow handle(s)
        while (workflow_get_children_count(handle) > 0)
        {
            ADUC_WorkflowHandle child = workflow_remove_child(handle, 0);
            workflow_free(child);
        }

        Log_Debug("Creating workflow for %lu step(s). Parent's level: %d", stepCount, workflowLevel);
        for (size_t i = 0; i < stepCount; i++)
        {
            STRING_HANDLE childId = nullptr;
            childHandle = nullptr;
            ADUC_FileEntity entity;
            memset(&entity, 0, sizeof(entity));

            if (workflow_is_inline_step(handle, i))
            {
                const char* selectedComponents = workflow_peek_selected_components(handle);

                Log_Debug(
                    "Creating workflow for level#%d step#%d.\nSelected components:\n=====\n%s\n=====\n",
                    workflowLevel,
                    i,
                    selectedComponents);

                // Create child workflow using inline step data.
                result = workflow_create_from_inline_step(handle, i, &childHandle);

                if (IsAducResultCodeSuccess(result.ResultCode))
                {
                    workflow_set_step_index(childHandle, i);

                    // Inherit parent's selected components.
                    workflow_set_selected_components(childHandle, workflow_peek_selected_components(handle));
                }
            }
            else
            {
                // Download detached update manifest file.
                if (!workflow_get_step_detached_manifest_file(handle, i, &entity))
                {
                    result.ResultCode = ADUC_Result_Failure;
                    result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_GET_FILE_ENTITY_FAILURE;
                    Log_Error(
                        "Cannot get a detached Update manifest file entity for level#%d step#%d", workflowLevel, i);
                    goto done;
                }

                Log_Info(
                    "Downloading a detached Update manifest file for level#%d step#%d (file id:%s).",
                    workflowLevel,
                    i,
                    entity.FileId);

                try
                {
                    result = ExtensionManager::Download(
                        &entity, handle, &Default_ExtensionManager_Download_Options, nullptr);
                }
                catch (...)
                {
                    Log_Error(
                        "Exception occurred while downloading a detached Update Manifest file for level#%d step#%lu (file id:%s).",
                        workflowLevel,
                        i,
                        entity.FileId);

                    result.ResultCode = ADUC_Result_Failure;
                    result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_DOWNLOAD_FAILURE_UNKNOWNEXCEPTION;
                }

                std::stringstream childManifestFile;
                childManifestFile << workFolder << "/" << entity.TargetFilename;

                ADUC_FileEntity_Uninit(&entity);

                // For 'microsoft/steps:1' implementation, abort download task as soon as an error occurs.
                if (IsAducResultCodeFailure(result.ResultCode))
                {
                    Log_Error(
                        "An error occurred while downloading manifest file for step#%lu (erc:%d)",
                        i,
                        result.ExtendedResultCode);
                    goto done;
                }

                // Create child workflow from file.
                result = workflow_init_from_file(childManifestFile.str().c_str(), false, &childHandle);

                if (IsAducResultCodeSuccess(result.ResultCode))
                {
                    workflow_set_step_index(childHandle, i);

                    // If no component enumerator is registered, assume that this reference update is for the host device.
                    // Don't set selected components in the workflow data.
                    if (ExtensionManager::IsComponentsEnumeratorRegistered())
                    {
                        // Select components based on the first pair of compatibility properties.
                        ADUC::StringUtils::cstr_wrapper compatibilityString{
                            workflow_get_update_manifest_compatibility(childHandle, 0)
                        };
                        JSON_Value* compsValue = nullptr;
                        if (compatibilityString.get() == nullptr)
                        {
                            Log_Error("Cannot get compatibility info for components-update #%lu", i);
                            result.ResultCode = ADUC_Result_Failure;
                            result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_GET_REF_STEP_COMPATIBILITY_FAILED;
                            goto done;
                        }

                        std::string output;
                        result = ExtensionManager::SelectComponents(compatibilityString.get(), output);

                        if (IsAducResultCodeFailure(result.ResultCode))
                        {
                            Log_Error("Cannot select components for components-update #%lu", i);
                            goto done;
                        }

                        compsValue = json_parse_string(output.c_str());
                        json_value_free(compsValue);

                        if (!workflow_set_selected_components(childHandle, output.c_str()))
                        {
                            result.ResultCode = ADUC_Result_Failure;
                            result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_SET_SELECTED_COMPONENTS_FAILURE;
                        }

                        Log_Debug(
                            "Set child handle's selected components: %s",
                            workflow_peek_selected_components(childHandle));
                    }
                }
            }

            if (IsAducResultCodeFailure(result.ResultCode))
            {
                Log_Error("ERROR: failed to create workflow for level:%d step#%d.", workflowLevel, i);
                goto done;
            }
            else
            {
                childId = STRING_construct_sprintf("%lu", i);
                workflow_set_id(childHandle, STRING_c_str(childId));
                STRING_delete(childId);
                childId = nullptr;
#if _ADU_DEBUG
                char* childManifest = workflow_get_serialized_update_manifest(childHandle, true);
                Log_Debug(
                    "##########\n# Successfully created workflow object for child#%s\n# Handle:0x%x\n# Manifest:\n%s\n",
                    workflow_peek_id(childHandle),
                    childHandle,
                    childManifest);
                workflow_free_string(childManifest);
#endif
            }

            if (!workflow_insert_child(handle, -1, childHandle))
            {
                result.ResultCode = ADUC_Result_Failure;
                result.ExtendedResultCode = ADUC_ERC_STEPS_HANDLER_CHILD_WORKFLOW_INSERT_FAILED;
                goto done;
            }
        }
    }

    result = { ADUC_Result_Success };

done:
    workflow_free_string(workFolder);

    if (IsAducResultCodeFailure(result.ResultCode))
    {
        workflow_free(childHandle);
    }

    return result;
}