Common::ErrorCode PartitionedServiceDescriptor::FromPublicApi()

in src/prod/src/ServiceModel/naming/PartitionedServiceDescriptor.cpp [1298:1821]


    Common::ErrorCode PartitionedServiceDescriptor::FromPublicApi(FABRIC_SERVICE_DESCRIPTION const & serviceDescription)
    {
        if (serviceDescription.Value == NULL) 
        { 
            Trace.WriteWarning(TraceComponent, "Invalid NULL parameter: serviceDescription.Description");
            return ErrorCode::FromHResult(E_POINTER); 
        }

        std::wstring applicationName;
        std::wstring name;
        ServiceTypeIdentifier typeIdentifier;
        int partitionCount;
        int targetReplicaSetSize;
        int minReplicaSetSize;
        bool isStateful;
        bool hasPersistedState;
        TimeSpan replicaRestartWaitDuration(TimeSpan::MinValue);
        TimeSpan quorumLossWaitDuration(TimeSpan::MinValue);
        TimeSpan standByReplicaKeepDuration(TimeSpan::MinValue);
        std::vector<BYTE> initializationData;
        std::vector<Reliability::ServiceCorrelationDescription> correlations;
        std::wstring placementConstraints;
        std::vector<Reliability::ServiceLoadMetricDescription> metrics;
        std::vector<ServiceModel::ServicePlacementPolicyDescription> placementPolicies;
        FABRIC_MOVE_COST defaultMoveCost = FABRIC_MOVE_COST_LOW;
        ServicePackageActivationMode::Enum servicePackageActivationMode = ServicePackageActivationMode::SharedProcess;

        FABRIC_PARTITION_SCHEME partitionScheme;
        void * partitionDescription;
        __int64 lowKeyInt64 = -1;
        __int64 highKeyInt64 = -1;
        std::vector<std::wstring> partitionNames;

        std::wstring serviceDnsName;

        std::vector<Reliability::ServiceScalingPolicyDescription> scalingPolicies;

        switch (serviceDescription.Kind)
        {
        case FABRIC_SERVICE_DESCRIPTION_KIND_STATELESS:
        {
            auto stateless = reinterpret_cast<FABRIC_STATELESS_SERVICE_DESCRIPTION*>(
                serviceDescription.Value);

            HRESULT hr = StringUtility::LpcwstrToWstring(stateless->ServiceName, false /*acceptNull*/, ParameterValidator::MinStringSize, CommonConfig::GetConfig().MaxNamingUriLength, name);
            if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }

            hr = StringUtility::LpcwstrToWstring(stateless->ApplicationName, true /*acceptNull*/, applicationName);
            if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }

            wstring serviceTypeName;
            hr = StringUtility::LpcwstrToWstring(stateless->ServiceTypeName, false /*acceptNull*/, serviceTypeName);
            if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }

            ErrorCode error = ServiceTypeIdentifier::FromString(serviceTypeName, typeIdentifier);
            if (!error.IsSuccess())
            {
                Trace.WriteWarning(TraceComponent, "Could not parse ServiceTypeName '{0}'", stateless->ServiceTypeName);
                return error;
            }

            if (stateless->CorrelationCount > 0 && stateless->Correlations == NULL)
            {
                Trace.WriteWarning(TraceComponent, "Invalid NULL parameter: Service correlations");
                return ErrorCode::FromHResult(E_POINTER);
            }
            for (ULONG i = 0; i < stateless->CorrelationCount; i++)
            {
                wstring correlationServiceName;
                hr = StringUtility::LpcwstrToWstring(stateless->Correlations[i].ServiceName, true /*acceptNull*/, 0, CommonConfig::GetConfig().MaxNamingUriLength, correlationServiceName);
                if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }

                NamingUri correlationServiceNameUri;
                if (!NamingUri::TryParse(correlationServiceName, correlationServiceNameUri))
                {
                    Trace.WriteWarning(TraceComponent, "Could not parse ServiceName '{0}' in correlation description", correlationServiceName);
                    return ErrorCode(ErrorCodeValue::InvalidArgument);
                }

                // strip of the fragment (i.e. service group member names)
                if (!correlationServiceNameUri.Fragment.empty())
                {
                    correlationServiceNameUri = NamingUri(correlationServiceNameUri.Path);
                }

                correlations.push_back(Reliability::ServiceCorrelationDescription(
                    correlationServiceNameUri.Name,
                    stateless->Correlations[i].Scheme));
            }

            isPlacementConstraintsValid_ = (stateless->PlacementConstraints != nullptr);

            hr = StringUtility::LpcwstrToWstring(stateless->PlacementConstraints, true /*acceptNull*/, placementConstraints);
            if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }

            if (stateless->MetricCount > 0 && stateless->Metrics == NULL)
            {
                Trace.WriteWarning(TraceComponent, "Invalid NULL parameter: Service description metrics");
                return ErrorCode::FromHResult(E_POINTER);
            }

            for (ULONG i = 0; i < stateless->MetricCount; i++)
            {
                wstring metricsName;
                hr = StringUtility::LpcwstrToWstring(stateless->Metrics[i].Name, true /*acceptNull*/, metricsName);
                if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }
                metrics.push_back(Reliability::ServiceLoadMetricDescription(
                    move(metricsName),
                    stateless->Metrics[i].Weight,
                    stateless->Metrics[i].PrimaryDefaultLoad,
                    stateless->Metrics[i].SecondaryDefaultLoad));
            }

            initializationData = std::vector<byte>(
                stateless->InitializationData,
                stateless->InitializationData + stateless->InitializationDataSize);
            targetReplicaSetSize = stateless->InstanceCount;
            minReplicaSetSize = 1;
            isStateful = false;
            hasPersistedState = false;
            partitionScheme = stateless->PartitionScheme;
            partitionDescription = stateless->PartitionSchemeDescription;

            if (stateless->Reserved == NULL)
            {
                break;
            }

            auto statelessEx1 = reinterpret_cast<FABRIC_STATELESS_SERVICE_DESCRIPTION_EX1*>(stateless->Reserved);
            if (statelessEx1->PolicyList != NULL)
            {
                isPlacementPolicyValid_ = true;
                auto pList = statelessEx1->PolicyList;
                if (pList->PolicyCount > 0 && pList->Policies == NULL)
                {
                    Trace.WriteWarning("PartitionedServiceDescription", "Invalid NULL parameter: Service placement policy");
                    return ErrorCode::FromHResult(E_POINTER);
                }

                for (ULONG i = 0; i < pList->PolicyCount; i++)
                {
                    std::wstring domainName;
                    FABRIC_SERVICE_PLACEMENT_POLICY_DESCRIPTION & policyDesc = pList->Policies[i];
                    ServicePlacementPolicyHelper::PolicyDescriptionToDomainName(policyDesc, domainName);
                    placementPolicies.push_back(ServiceModel::ServicePlacementPolicyDescription(move(domainName), policyDesc.Type));
                }
            }

            if (statelessEx1->Reserved == NULL)
            {
                break;
            }

            auto statelessEx2 = reinterpret_cast<FABRIC_STATELESS_SERVICE_DESCRIPTION_EX2*>(statelessEx1->Reserved);
            if (statelessEx2->IsDefaultMoveCostSpecified)
            {
                defaultMoveCost = statelessEx2->DefaultMoveCost;
            }

            if (statelessEx2->Reserved == NULL)
            {
                break;
            }

            auto statelessEx3 = reinterpret_cast<FABRIC_STATELESS_SERVICE_DESCRIPTION_EX3*>(statelessEx2->Reserved);

            error = ServicePackageActivationMode::FromPublicApi(statelessEx3->ServicePackageActivationMode, servicePackageActivationMode);
            if (!error.IsSuccess())
            {
                Trace.WriteWarning(
                    TraceComponent,
                    "ReplicaIsolationLevel::FromPublicApi failed. statelessEx3->ServicePackageActivationMode='{0}'",
                    static_cast<ULONG>(statelessEx3->ServicePackageActivationMode));

		 return error;
            }

            hr = StringUtility::LpcwstrToWstring(statelessEx3->ServiceDnsName, true /*acceptNull*/, serviceDnsName);
            if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }
           
            if (statelessEx3->Reserved == NULL)
            {
                break;
            }

            auto statelessEx4 = reinterpret_cast<FABRIC_STATELESS_SERVICE_DESCRIPTION_EX4*>(statelessEx3->Reserved);

            if (statelessEx4->ScalingPolicyCount > 1)
            {
                // Currently, only one scaling policy is allowed per service.
                // Vector is there for future uses (when services could have multiple scaling policies).
                return TraceAndGetErrorDetails(ErrorCodeValue::InvalidServiceScalingPolicy, wformatString(GET_NS_RC(ScalingPolicy_Scaling_Count), statelessEx4->ScalingPolicyCount));
            }

            for (ULONG i = 0; i < statelessEx4->ScalingPolicyCount; i++)
            {
                Reliability::ServiceScalingPolicyDescription scalingDescription;
                auto scalingError = scalingDescription.FromPublicApi(statelessEx4->ServiceScalingPolicies[i]);
                if (!scalingError.IsSuccess())
                {
                    return scalingError;
                }
                scalingPolicies.push_back(move(scalingDescription));
            }

            break;
        }

        case FABRIC_SERVICE_DESCRIPTION_KIND_STATEFUL:
            {
                auto stateful = reinterpret_cast<FABRIC_STATEFUL_SERVICE_DESCRIPTION*>(
                    serviceDescription.Value);

                HRESULT hr = StringUtility::LpcwstrToWstring(stateful->ServiceName, false /*acceptNull*/, ParameterValidator::MinStringSize, CommonConfig::GetConfig().MaxNamingUriLength, name);
                if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }

                hr = StringUtility::LpcwstrToWstring(stateful->ApplicationName, true /*acceptNull*/, applicationName);
                if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }
                                
                wstring serviceTypeName;
                hr = StringUtility::LpcwstrToWstring(stateful->ServiceTypeName, false /*acceptNull*/, serviceTypeName);
                if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }
                
                ErrorCode error = ServiceTypeIdentifier::FromString(serviceTypeName, typeIdentifier);
                if (!error.IsSuccess())
                {
                    Trace.WriteWarning(TraceComponent, "Could not parse ServiceTypeName '{0}'", stateful->ServiceTypeName);
                    return error;
                }

                if (stateful->CorrelationCount > 0 && stateful->Correlations == NULL) 
                { 
                    Trace.WriteWarning(TraceComponent, "Invalid NULL parameter: Service correlations");
                    return ErrorCode::FromHResult(E_POINTER); 
                }
                for(ULONG i = 0; i < stateful->CorrelationCount; i++)
                {
                    wstring correlationServiceName;
                    hr = StringUtility::LpcwstrToWstring(stateful->Correlations[i].ServiceName, true /*acceptNull*/, 0, CommonConfig::GetConfig().MaxNamingUriLength, correlationServiceName);
                    if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }

                    NamingUri correlationServiceNameUri;
                    if (!NamingUri::TryParse(correlationServiceName, correlationServiceNameUri))
                    {
                        Trace.WriteWarning(TraceComponent, "Could not parse ServiceName '{0}' in correlation description", correlationServiceName);
                        return ErrorCode(ErrorCodeValue::InvalidArgument);
                    }
                    
                    // strip of the fragment (i.e. service group member names)
                    if (!correlationServiceNameUri.Fragment.empty())
                    {
                        correlationServiceNameUri = NamingUri(correlationServiceNameUri.Path);
                    }

                    correlations.push_back(Reliability::ServiceCorrelationDescription(
                        correlationServiceNameUri.Name, 
                        stateful->Correlations[i].Scheme));
                }

                isPlacementConstraintsValid_ = (stateful->PlacementConstraints != nullptr);

                hr = StringUtility::LpcwstrToWstring(stateful->PlacementConstraints, true /*acceptNull*/, placementConstraints);
                if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }
                
                if (stateful->MetricCount > 0 && stateful->Metrics == NULL) 
                { 
                    Trace.WriteWarning(TraceComponent, "Invalid NULL parameter: Service description metrics");
                    return ErrorCode::FromHResult(E_POINTER);
                }

                for(ULONG i = 0; i < stateful->MetricCount; i++)
                {
                    wstring metricsName;
                    hr = StringUtility::LpcwstrToWstring(stateful->Metrics[i].Name, true /*acceptNull*/, metricsName);
                    if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }
                    metrics.push_back(Reliability::ServiceLoadMetricDescription(
                        move(metricsName), 
                        stateful->Metrics[i].Weight,
                        stateful->Metrics[i].PrimaryDefaultLoad,
                        stateful->Metrics[i].SecondaryDefaultLoad));
                }

                initializationData = std::vector<byte>(
                    stateful->InitializationData,
                    stateful->InitializationData + stateful->InitializationDataSize);
                targetReplicaSetSize = stateful->TargetReplicaSetSize;
                minReplicaSetSize = stateful->MinReplicaSetSize;
                isStateful = true;
                hasPersistedState = stateful->HasPersistedState ? true : false;
                partitionScheme = stateful->PartitionScheme;
                partitionDescription = stateful->PartitionSchemeDescription;

                if (stateful->Reserved == NULL)
                {
                    break;
                }

                auto statefulEx1 = reinterpret_cast<FABRIC_STATEFUL_SERVICE_DESCRIPTION_EX1*>(stateful->Reserved);

                if (statefulEx1->PolicyList != NULL)
                {
                    isPlacementPolicyValid_ = true;
                    auto pList = statefulEx1->PolicyList;
                    if (pList->PolicyCount > 0 && pList->Policies == NULL)
                    {
                        Trace.WriteWarning("PartitionedServiceDescription", "Invalid NULL parameter: Service placement policy");
                        return ErrorCode::FromHResult(E_POINTER);
                    }

                    for (ULONG i = 0; i < pList->PolicyCount; i++)
                    {
                        std::wstring domainName;
                        FABRIC_SERVICE_PLACEMENT_POLICY_DESCRIPTION & policyDesc = pList->Policies[i];
                        ServicePlacementPolicyHelper::PolicyDescriptionToDomainName(policyDesc, domainName);
                        placementPolicies.push_back(ServiceModel::ServicePlacementPolicyDescription(move(domainName), policyDesc.Type));
                    }
                }

                if (statefulEx1->FailoverSettings != NULL)
                {
                    auto failoverSettings = statefulEx1->FailoverSettings;

                    if ((failoverSettings->Flags & FABRIC_STATEFUL_SERVICE_SETTINGS_REPLICA_RESTART_WAIT_DURATION) != 0)
                    {
                        replicaRestartWaitDuration = TimeSpan::FromSeconds(failoverSettings->ReplicaRestartWaitDurationSeconds);
                    }

                    if ((failoverSettings->Flags & FABRIC_STATEFUL_SERVICE_SETTINGS_QUORUM_LOSS_WAIT_DURATION) != 0)
                    {
                        quorumLossWaitDuration = TimeSpan::FromSeconds(failoverSettings->QuorumLossWaitDurationSeconds);
                    }

                    if (failoverSettings->Reserved != NULL)
                    {
                        auto failoverSettingsEx1 = reinterpret_cast<FABRIC_STATEFUL_SERVICE_FAILOVER_SETTINGS_EX1*>(failoverSettings->Reserved);
                        if (failoverSettingsEx1 == NULL)
                        {
                            return ErrorCode::FromHResult(E_INVALIDARG);
                        }

                        if ((failoverSettings->Flags & FABRIC_STATEFUL_SERVICE_SETTINGS_STANDBY_REPLICA_KEEP_DURATION) != 0)
                        {
                            standByReplicaKeepDuration = TimeSpan::FromSeconds(failoverSettingsEx1->StandByReplicaKeepDurationSeconds);
                        }

                    }
                }

                if (statefulEx1->Reserved == NULL)
                {
                    break;
                }

                auto statefulEx2 = reinterpret_cast<FABRIC_STATEFUL_SERVICE_DESCRIPTION_EX2*>(statefulEx1->Reserved);
                if (statefulEx2->IsDefaultMoveCostSpecified)
                {
                    defaultMoveCost = statefulEx2->DefaultMoveCost;
                }

                if (statefulEx2->Reserved == NULL)
                {
                    break;
                }

                auto statefulEx3 = reinterpret_cast<FABRIC_STATEFUL_SERVICE_DESCRIPTION_EX3*>(statefulEx2->Reserved);

                error = ServicePackageActivationMode::FromPublicApi(statefulEx3->ServicePackageActivationMode, servicePackageActivationMode);
                if (!error.IsSuccess())
                {
                    Trace.WriteWarning(
                        TraceComponent,
                        "ReplicaIsolationLevel::FromPublicApi failed. statefulEx3->ServicePackageActivationMode='{0}'",
                        static_cast<ULONG>(statefulEx3->ServicePackageActivationMode));

                    return error;
                }

                hr = StringUtility::LpcwstrToWstring(statefulEx3->ServiceDnsName, true /*acceptNull*/, serviceDnsName);
                if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }

                if (statefulEx3->Reserved == NULL)
                {
                    break;
                }

                auto statefulEx4 = reinterpret_cast<FABRIC_STATEFUL_SERVICE_DESCRIPTION_EX4*>(statefulEx3->Reserved);

                if (statefulEx4->ScalingPolicyCount > 1)
                {
                    // Currently, only one scaling policy is allowed per service.
                    // Vector is there for future uses (when services could have multiple scaling policies).
                    return TraceAndGetErrorDetails(ErrorCodeValue::InvalidServiceScalingPolicy, wformatString(GET_NS_RC(ScalingPolicy_Scaling_Count), statefulEx4->ScalingPolicyCount));
                }

                for (ULONG i = 0; i < statefulEx4->ScalingPolicyCount; i++)
                {
                    Reliability::ServiceScalingPolicyDescription scalingDescription;
                    auto scalingError = scalingDescription.FromPublicApi(statefulEx4->ServiceScalingPolicies[i]);
                    if (!scalingError.IsSuccess())
                    {
                        return scalingError;
                    }
                    scalingPolicies.push_back(move(scalingDescription));
                }
            }
            break;

        default: 
            return ErrorCode::FromHResult(E_INVALIDARG);
        }

        switch (partitionScheme)
        {
        case FABRIC_PARTITION_SCHEME_SINGLETON:
            partitionCount = 1;
            break;

        case FABRIC_PARTITION_SCHEME_UNIFORM_INT64_RANGE:
            {
                if (partitionDescription == NULL)
                {
                    Trace.WriteWarning(
                        TraceComponent, 
                        "Partition description cannot be NULL for service: type = {0} name = {1}",
                        typeIdentifier,
                        name);

                    return ErrorCode::FromHResult(E_POINTER);
                }

                auto d = reinterpret_cast<FABRIC_UNIFORM_INT64_RANGE_PARTITION_SCHEME_DESCRIPTION*>(partitionDescription);
                partitionCount = d->PartitionCount;
                lowKeyInt64 = d->LowKey;
                highKeyInt64 = d->HighKey;
                break;
            }

        case FABRIC_PARTITION_SCHEME_NAMED:
            {
                if (partitionDescription == NULL)
                {
                    Trace.WriteWarning(
                        TraceComponent, 
                        "Partition description cannot be NULL for service: type = {0} name = {1}",
                        typeIdentifier,
                        name);

                    return ErrorCode::FromHResult(E_POINTER);
                }

                auto d = reinterpret_cast<FABRIC_NAMED_PARTITION_SCHEME_DESCRIPTION*>(partitionDescription);
                partitionCount = d->PartitionCount;
                auto hr = StringUtility::FromLPCWSTRArray(partitionCount, d->Names, partitionNames);
                if (FAILED(hr)) { return ErrorCode::FromHResult(hr); }
                break;
            }

        default:
            return (partitionDescription != NULL) ? ErrorCode::FromHResult(E_INVALIDARG) : ErrorCode::FromHResult(E_POINTER);
        }

        NamingUri nameUri;
        if (!NamingUri::TryParse(name, nameUri))
        {
            return TraceAndGetErrorDetails(ErrorCodeValue::InvalidNameUri, wformatString("{0} {1}", GET_NS_RC(Invalid_Uri), name));
        }

        auto error = ValidateServiceParameters(
            name,
            placementConstraints,
            metrics,
            correlations,
            scalingPolicies,
            applicationName,
            typeIdentifier,
            partitionCount,
            targetReplicaSetSize,
            minReplicaSetSize,
            isStateful,
            partitionScheme,
            lowKeyInt64,
            highKeyInt64,
            partitionNames);

        if (!error.IsSuccess())
        {
            return error;
        }

        Reliability::ServiceDescription description(
            nameUri.ToString(),
            0, // instance
            0, // UpdateVersion
            partitionCount,
            targetReplicaSetSize,
            minReplicaSetSize,
            isStateful,
            hasPersistedState,
            replicaRestartWaitDuration,
            quorumLossWaitDuration,
            standByReplicaKeepDuration,
            typeIdentifier,
            correlations,
            placementConstraints,
            0, // ScaleoutCount
            metrics,
            defaultMoveCost, // DefaultMoveCost
            initializationData,
            applicationName,
            placementPolicies,
            servicePackageActivationMode,
            serviceDnsName,
            scalingPolicies);

        service_ = move(description);
        partitionScheme_ = partitionScheme;
        lowRange_ = lowKeyInt64;
        highRange_ = highKeyInt64;
        partitionNames_ = partitionNames;

        GenerateRandomCuids();

        return ErrorCode(ErrorCodeValue::Success);
    }