diff --git a/aws_quickstart/CHANGELOG.md b/aws_quickstart/CHANGELOG.md index 2d00d62c..dad0674d 100644 --- a/aws_quickstart/CHANGELOG.md +++ b/aws_quickstart/CHANGELOG.md @@ -1,3 +1,7 @@ +# 4.19.1 (August 25, 2026) + +- Fix Datadog Operator Marketplace agreement discovery and acceptance for Lambda runtimes that lack required agreement operations or paginators. + # 4.19.0 (August 21, 2026) - Accept the free Datadog Operator AWS Marketplace agreement in the commercial AWS partition when EKS instrumentation is selected, allowing the managed add-on installation to proceed without Marketplace permissions on the Datadog integration role. GovCloud and China deployments skip automatic agreement acceptance. diff --git a/aws_quickstart/accept_operator_subscription.py b/aws_quickstart/accept_operator_subscription.py index 1dca8c77..31ba91db 100644 --- a/aws_quickstart/accept_operator_subscription.py +++ b/aws_quickstart/accept_operator_subscription.py @@ -10,7 +10,7 @@ import cfnresponse from cfn_common import send_cfn_response - +from marketplace_agreement_compat import apply_marketplace_agreement_compatibility LOGGER = logging.getLogger() LOGGER.setLevel(logging.INFO) @@ -612,6 +612,7 @@ def wait_for_entitlement( def ensure_subscription(event, *, deadline=None): try: session = boto3.Session() + compatibility_applied = apply_marketplace_agreement_compatibility(session) discovery_client = session.client( "marketplace-discovery", region_name=MARKETPLACE_REGION, @@ -634,6 +635,7 @@ def ensure_subscription(event, *, deadline=None): "succeeded", "clients_created", boto3_version=getattr(boto3, "__version__", "unknown"), + marketplace_compatibility_model_applied=compatibility_applied, ) agreement_id = find_active_agreement(agreement_client, deadline=deadline) diff --git a/aws_quickstart/accept_operator_subscription_test.py b/aws_quickstart/accept_operator_subscription_test.py index 7d135950..4ec1ef54 100644 --- a/aws_quickstart/accept_operator_subscription_test.py +++ b/aws_quickstart/accept_operator_subscription_test.py @@ -32,6 +32,7 @@ validate_zero_charge_summary, wait_for_entitlement, ) +from marketplace_agreement_compat import apply_marketplace_agreement_compatibility def paginator_client(**operation_pages): @@ -166,11 +167,88 @@ def test_release_embeds_subscription_source(self): self.assertIn( "embed_python_source_with_common datadog_integration_permissions.yaml " - "accept_operator_subscription.py ACCEPT_OPERATOR_SUBSCRIPTION_SOURCE", + "accept_operator_subscription.py ACCEPT_OPERATOR_SUBSCRIPTION_SOURCE " + "marketplace_agreement_compat.py", release, ) +class TestMarketplaceAgreementCompatibility(unittest.TestCase): + def test_adds_missing_operations_and_paginators(self): + session = MagicMock() + runtime_shape = {"runtime": True} + service_data = { + "operations": {"SearchAgreements": {}}, + "shapes": {"ResourceId": runtime_shape}, + } + paginator_config = {} + session._session.get_service_data.return_value = service_data + session._session.get_paginator_model.return_value._paginator_config = ( + paginator_config + ) + + applied = apply_marketplace_agreement_compatibility(session) + + self.assertTrue(applied) + self.assertIn("CreateAgreementRequest", service_data["operations"]) + self.assertIn("AcceptAgreementRequest", service_data["operations"]) + self.assertIn("GetAgreementEntitlements", service_data["operations"]) + self.assertIn("CreateAgreementRequestInput", service_data["shapes"]) + self.assertIn("AcceptAgreementRequestOutput", service_data["shapes"]) + self.assertIn("GetAgreementEntitlementsInput", service_data["shapes"]) + self.assertIn("GetAgreementEntitlementsOutput", service_data["shapes"]) + self.assertIs(service_data["shapes"]["ResourceId"], runtime_shape) + self.assertIn("GetAgreementEntitlements", paginator_config) + self.assertIn("SearchAgreements", paginator_config) + + def test_preserves_runtime_model_when_definitions_are_available(self): + session = MagicMock() + operations = { + "CreateAgreementRequest": {"runtime": True}, + "AcceptAgreementRequest": {"runtime": True}, + "GetAgreementEntitlements": {"runtime": True}, + } + paginator_config = { + "GetAgreementEntitlements": {"runtime": True}, + "SearchAgreements": {"runtime": True}, + } + service_data = {"operations": operations.copy(), "shapes": {}} + session._session.get_service_data.return_value = service_data + session._session.get_paginator_model.return_value._paginator_config = ( + paginator_config.copy() + ) + + applied = apply_marketplace_agreement_compatibility(session) + + self.assertFalse(applied) + self.assertEqual(service_data["operations"], operations) + self.assertEqual(service_data["shapes"], {}) + + def test_adds_only_missing_paginator_to_runtime_model(self): + session = MagicMock() + operations = { + "CreateAgreementRequest": {"runtime": True}, + "AcceptAgreementRequest": {"runtime": True}, + "GetAgreementEntitlements": {"runtime": True}, + } + shapes = {"RuntimeShape": {"runtime": True}} + service_data = {"operations": operations.copy(), "shapes": shapes.copy()} + search_paginator = {"runtime": True} + paginator_config = {"SearchAgreements": search_paginator} + session._session.get_service_data.return_value = service_data + session._session.get_paginator_model.return_value._paginator_config = ( + paginator_config + ) + + applied = apply_marketplace_agreement_compatibility(session) + + self.assertTrue(applied) + self.assertEqual(service_data["operations"], operations) + self.assertEqual(service_data["shapes"], shapes) + self.assertIn("GetAgreementEntitlements", paginator_config) + self.assertIs(paginator_config["SearchAgreements"], search_paginator) + + class TestAgreementDiscovery(unittest.TestCase): def test_returns_active_agreement(self): client = paginator_client( @@ -200,15 +278,11 @@ def test_returns_none_when_no_active_agreement_exists(self): self.assertIsNone(find_active_agreement(client)) - def test_rejects_multiple_active_agreements(self): + def test_rejects_multiple_active_agreements_across_pages(self): client = paginator_client( search_agreements=[ - { - "agreementViewSummaries": [ - {"agreementId": "agreement-1"}, - {"agreementId": "agreement-2"}, - ] - } + {"agreementViewSummaries": [{"agreementId": "agreement-1"}]}, + {"agreementViewSummaries": [{"agreementId": "agreement-2"}]}, ] ) @@ -667,8 +741,14 @@ def test_request_acceptance_failure(self): class TestClientInitialization(unittest.TestCase): + @patch( + "accept_operator_subscription.apply_marketplace_agreement_compatibility", + return_value=False, + ) @patch("accept_operator_subscription.boto3.Session") - def test_reports_runtime_without_marketplace_discovery(self, mock_session): + def test_reports_runtime_without_marketplace_discovery( + self, mock_session, _mock_compatibility + ): mock_session.return_value.client.side_effect = RuntimeError("UnknownServiceError") with self.assertRaises(SubscriptionError) as raised: diff --git a/aws_quickstart/marketplace_agreement_compat.py b/aws_quickstart/marketplace_agreement_compat.py new file mode 100644 index 00000000..cf95b806 --- /dev/null +++ b/aws_quickstart/marketplace_agreement_compat.py @@ -0,0 +1,525 @@ +# Lambda runtimes can lag newly released AWS operations. Add these API definitions +# to botocore so it retains its normal validation, signing, retries, and parsing. +# The definitions mirror the AWS Marketplace Agreement 2020-03-01 service model. +_SERVICE_MODEL_PATCH = { + "operations": { + "CreateAgreementRequest": { + "name": "CreateAgreementRequest", + "http": {"method": "POST", "requestUri": "/"}, + "input": {"shape": "CreateAgreementRequestInput"}, + "output": {"shape": "CreateAgreementRequestOutput"}, + "errors": [ + {"shape": "AccessDeniedException"}, + {"shape": "ValidationException"}, + {"shape": "ResourceNotFoundException"}, + {"shape": "ThrottlingException"}, + {"shape": "ServiceQuotaExceededException"}, + {"shape": "InternalServerException"}, + {"shape": "ConflictException"}, + ], + }, + "AcceptAgreementRequest": { + "name": "AcceptAgreementRequest", + "http": {"method": "POST", "requestUri": "/"}, + "input": {"shape": "AcceptAgreementRequestInput"}, + "output": {"shape": "AcceptAgreementRequestOutput"}, + "errors": [ + {"shape": "AccessDeniedException"}, + {"shape": "ValidationException"}, + {"shape": "ResourceNotFoundException"}, + {"shape": "ThrottlingException"}, + {"shape": "InternalServerException"}, + {"shape": "ConflictException"}, + ], + }, + "GetAgreementEntitlements": { + "name": "GetAgreementEntitlements", + "http": {"method": "POST", "requestUri": "/"}, + "input": {"shape": "GetAgreementEntitlementsInput"}, + "output": {"shape": "GetAgreementEntitlementsOutput"}, + "errors": [ + {"shape": "AccessDeniedException"}, + {"shape": "ValidationException"}, + {"shape": "ResourceNotFoundException"}, + {"shape": "ThrottlingException"}, + {"shape": "InternalServerException"}, + ], + "readonly": True, + }, + }, + "shapes": { + "AcceptAgreementRequestInput": { + "type": "structure", + "required": ["agreementRequestId"], + "members": { + "agreementRequestId": {"shape": "AgreementRequestId"}, + "purchaseOrders": {"shape": "PurchaseOrders"}, + }, + }, + "AcceptAgreementRequestOutput": { + "type": "structure", + "members": {"agreementId": {"shape": "ResourceId"}}, + }, + "AccessDeniedException": { + "type": "structure", + "members": { + "requestId": {"shape": "RequestId"}, + "message": {"shape": "ExceptionMessage"}, + "reason": {"shape": "AccessDeniedExceptionReason"}, + }, + "exception": True, + }, + "AccessDeniedExceptionReason": { + "type": "string", + "enum": [ + "INVALID_ACCOUNT_STATE", + "DENIED_BY_PRIVATE_MARKETPLACE_POLICY", + "FAILED_KYC_COMPLIANCE", + "MISSING_MFA", + "INVALID_ACCESS", + ], + }, + "AgreementEntitlement": { + "type": "structure", + "members": { + "resource": {"shape": "Resource"}, + "type": {"shape": "EntitlementType"}, + "registrationToken": {"shape": "RegistrationToken"}, + "status": {"shape": "AgreementEntitlementStatus"}, + "statusReasonCode": {"shape": "AgreementEntitlementStatusReasonCode"}, + "licenseArn": {"shape": "AwsArn"}, + }, + }, + "AgreementEntitlementList": { + "type": "list", + "member": {"shape": "AgreementEntitlement"}, + }, + "AgreementEntitlementStatus": { + "type": "string", + "enum": [ + "PROVISIONED", + "SCHEDULED", + "PENDING", + "FAILED", + "DEPROVISIONED", + ], + }, + "AgreementEntitlementStatusReasonCode": { + "type": "string", + "enum": [ + "PROVISIONING_IN_PROGRESS", + "FUTURE_START_DATE", + "INVALID_PAYMENT_INSTRUMENT", + "INCOMPATIBLE_CURRENCY", + "ACCOUNT_SUSPENDED", + "UNSUPPORTED_OPERATION", + "AGREEMENT_INACTIVE", + "AGREEMENT_ACTIVE", + "PRODUCT_RESTRICTED", + ], + }, + "AgreementProposalId": { + "type": "string", + "max": 64, + "min": 1, + "pattern": "(at-|ap-)[A-Za-z0-9]+", + }, + "AgreementRequestId": { + "type": "string", + "max": 64, + "min": 1, + "pattern": "ar-[A-Za-z0-9]+", + }, + "AgreementResourceType": { + "type": "string", + "max": 64, + "min": 1, + "pattern": "[a-zA-Z]+", + }, + "AwsArn": { + "type": "string", + "max": 2048, + "min": 1, + "pattern": "arn:aws[a-zA-Z-]*:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:" + "[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:" + "[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023}", + }, + "ChargeRevision": {"type": "long", "box": True, "min": 1}, + "ChargeSummary": { + "type": "structure", + "members": { + "currencyCode": {"shape": "CurrencyCode"}, + "newAgreementValue": {"shape": "BoundedString"}, + "newAgreementValueAfterTax": {"shape": "BoundedString"}, + "expectedCharges": {"shape": "ExpectedChargeList"}, + "estimatedTaxes": {"shape": "EstimatedTaxes"}, + "itemizedCharges": {"shape": "ItemizedChargeList"}, + "invoicingEntity": {"shape": "InvoicingEntity"}, + }, + }, + "ConflictException": { + "type": "structure", + "members": { + "requestId": {"shape": "RequestId"}, + "message": {"shape": "ExceptionMessage"}, + "resourceId": {"shape": "ResourceId"}, + "resourceType": {"shape": "ResourceType"}, + }, + "exception": True, + }, + "CreateAgreementRequestInput": { + "type": "structure", + "required": ["intent", "requestedTerms"], + "members": { + "clientToken": {"shape": "ClientToken", "idempotencyToken": True}, + "intent": {"shape": "Intent"}, + "requestedTerms": {"shape": "RequestedTermList"}, + "sourceAgreementIdentifier": {"shape": "ResourceId"}, + "agreementProposalIdentifier": {"shape": "AgreementProposalId"}, + "taxConfiguration": {"shape": "TaxConfiguration"}, + }, + }, + "CreateAgreementRequestOutput": { + "type": "structure", + "members": { + "agreementRequestId": {"shape": "AgreementRequestId"}, + "chargeSummary": {"shape": "ChargeSummary"}, + }, + }, + "EstimatedTaxes": { + "type": "structure", + "members": { + "breakdown": {"shape": "TaxBreakdown"}, + "totalAmount": {"shape": "BoundedString"}, + }, + }, + "ExpectedCharge": { + "type": "structure", + "members": { + "id": {"shape": "ResourceId"}, + "time": {"shape": "Timestamp"}, + "amount": {"shape": "BoundedString"}, + "amountAfterTax": {"shape": "BoundedString"}, + "timing": {"shape": "Timing"}, + "estimatedTaxes": {"shape": "EstimatedTaxes"}, + }, + }, + "EntitlementType": { + "type": "string", + "max": 64, + "min": 1, + "pattern": "[A-Za-z:]+", + }, + "ExpectedChargeList": {"type": "list", "member": {"shape": "ExpectedCharge"}}, + "GetAgreementEntitlementsInput": { + "type": "structure", + "required": ["agreementId"], + "members": { + "agreementId": {"shape": "ResourceId"}, + "maxResults": {"shape": "MaxResults"}, + "nextToken": {"shape": "NextToken"}, + }, + }, + "GetAgreementEntitlementsOutput": { + "type": "structure", + "members": { + "agreementEntitlements": {"shape": "AgreementEntitlementList"}, + "nextToken": {"shape": "NextToken"}, + }, + }, + "Integer": {"type": "integer", "box": True}, + "Intent": {"type": "string", "enum": ["NEW", "AMEND", "REPLACE"]}, + "InternalServerException": { + "type": "structure", + "members": { + "requestId": {"shape": "RequestId"}, + "message": {"shape": "ExceptionMessage"}, + }, + "exception": True, + "fault": True, + }, + "InvoicingEntity": { + "type": "structure", + "members": { + "legalName": {"shape": "BoundedString"}, + "branchName": {"shape": "BoundedString"}, + }, + }, + "ItemizedCharge": { + "type": "structure", + "members": { + "dimensionKey": {"shape": "BoundedString"}, + "newQuantity": {"shape": "Integer"}, + "oldQuantity": {"shape": "Integer"}, + "chargeReference": {"shape": "ResourceId"}, + "incrementalChargeAmount": {"shape": "BoundedString"}, + }, + }, + "ItemizedChargeList": {"type": "list", "member": {"shape": "ItemizedCharge"}}, + "MaxResults": {"type": "integer", "box": True, "max": 50, "min": 1}, + "NextToken": { + "type": "string", + "max": 8192, + "min": 0, + "pattern": "[a-zA-Z0-9+/=_-]+", + }, + "PurchaseOrder": { + "type": "structure", + "required": ["chargeId"], + "members": { + "chargeId": {"shape": "ResourceId"}, + "chargeRevision": {"shape": "ChargeRevision"}, + "agreementId": {"shape": "ResourceId"}, + "purchaseOrderReference": {"shape": "PurchaseOrderReference"}, + }, + }, + "PurchaseOrderReference": {"type": "string", "min": 1}, + "PurchaseOrders": { + "type": "list", + "member": {"shape": "PurchaseOrder"}, + "max": 86, + "min": 1, + }, + "RegistrationToken": { + "type": "string", + "max": 512, + "min": 1, + "pattern": "[A-Za-z0-9+/=.:_-]+", + }, + "RequestedTerm": { + "type": "structure", + "required": ["id"], + "members": { + "id": {"shape": "TermId"}, + "configuration": {"shape": "RequestedTermConfiguration"}, + }, + }, + "RequestedTermConfiguration": { + "type": "structure", + "members": { + "configurableUpfrontPricingTermConfiguration": { + "shape": "ConfigurableUpfrontPricingTermConfiguration" + }, + "renewalTermConfiguration": {"shape": "RenewalTermConfiguration"}, + "variablePaymentTermConfiguration": { + "shape": "VariablePaymentTermConfiguration" + }, + }, + "union": True, + }, + "RequestedTermList": { + "type": "list", + "member": {"shape": "RequestedTerm"}, + "max": 30, + "min": 1, + }, + "Resource": { + "type": "structure", + "members": { + "id": {"shape": "ResourceId"}, + "type": {"shape": "AgreementResourceType"}, + }, + }, + "ResourceNotFoundException": { + "type": "structure", + "members": { + "requestId": {"shape": "RequestId"}, + "message": {"shape": "ExceptionMessage"}, + "resourceId": {"shape": "ResourceId"}, + "resourceType": {"shape": "ResourceType"}, + }, + "exception": True, + }, + "ResourceType": { + "type": "string", + "enum": [ + "Agreement", + "AgreementRequest", + "AgreementProposal", + "Charge", + "PaymentRequest", + "Invoice", + "AgreementCancellationRequest", + "BillingAdjustmentRequest", + ], + }, + "ServiceQuotaExceededException": { + "type": "structure", + "members": { + "requestId": {"shape": "RequestId"}, + "message": {"shape": "ExceptionMessage"}, + "quotaCode": {"shape": "BoundedString"}, + "serviceCode": {"shape": "BoundedString"}, + "resourceType": {"shape": "BoundedString"}, + "resourceId": {"shape": "ResourceId"}, + }, + "exception": True, + }, + "TaxBreakdown": {"type": "list", "member": {"shape": "TaxBreakdownItem"}}, + "TaxBreakdownItem": { + "type": "structure", + "members": { + "amount": {"shape": "BoundedString"}, + "rate": {"shape": "BoundedString"}, + "type": {"shape": "BoundedString"}, + }, + }, + "TaxConfiguration": { + "type": "structure", + "members": {"taxEstimation": {"shape": "TaxEstimation"}}, + }, + "TaxEstimation": {"type": "string", "enum": ["DISABLED", "ENABLED"]}, + "ThrottlingException": { + "type": "structure", + "members": { + "requestId": {"shape": "RequestId"}, + "message": {"shape": "ExceptionMessage"}, + }, + "exception": True, + }, + "Timing": { + "type": "string", + "enum": ["ON_ACCEPTANCE", "SCHEDULED", "BILLING_PERIOD"], + }, + "ValidationException": { + "type": "structure", + "members": { + "requestId": {"shape": "RequestId"}, + "message": {"shape": "ExceptionMessage"}, + "reason": {"shape": "ValidationExceptionReason"}, + "fields": {"shape": "ValidationExceptionFieldList"}, + }, + "exception": True, + }, + "ValidationExceptionReason": { + "type": "string", + "enum": [ + "MISSING_BILLING_ADJUSTMENTS", + "BILLING_ADJUSTMENTS_LIMIT_EXCEEDED", + "MISSING_INVOICE_ID", + "INVALID_ADJUSTMENT_AMOUNT", + "MISSING_ADJUSTMENT_AMOUNT", + "INVALID_REASON_CODE", + "MISSING_REASON_CODE", + "MISSING_DESCRIPTION", + "INVALID_INVOICE_ADJUSTMENT_PERIOD", + "INVALID_CURRENCY_CODE", + "MISSING_CURRENCY_CODE", + "EXCEEDED_MAXIMUM_ADJUSTMENT_AMOUNT", + "MISSING_BILLING_ADJUSTMENT_REQUEST_ENTRY", + "MULTIPLE_AGREEMENT_IDS", + "INVALID_AGREEMENT_CANCELLATION_REQUEST_ID", + "MISSING_AGREEMENT_CANCELLATION_REQUEST_ID", + "MISSING_REASON", + "INVALID_REASON", + "INVALID_STATUS", + "INVALID_AGREEMENT_ID", + "MISSING_AGREEMENT_ID", + "INVALID_CATALOG", + "INVALID_FILTERS", + "INVALID_FILTER_NAME", + "MISSING_FILTER_NAME", + "INVALID_FILTER_VALUES", + "MISSING_FILTER_VALUES", + "INVALID_SORT_BY", + "INVALID_SORT_ORDER", + "INVALID_NEXT_TOKEN", + "INVALID_MAX_RESULTS", + "INVALID_TERM_ID", + "MISSING_TERM_ID", + "MISSING_NAME", + "INVALID_NAME", + "INVALID_DESCRIPTION", + "MISSING_CHARGE_AMOUNT", + "INVALID_CHARGE_AMOUNT", + "MISSING_PAYMENT_REQUEST_ID", + "INVALID_PAYMENT_REQUEST_ID", + "MISSING_PARTY_TYPE", + "INVALID_PARTY_TYPE", + "UNSUPPORTED_FILTERS", + "INVALID_CLIENT_TOKEN", + "INVALID_INTENT", + "MISSING_INTENT", + "INVALID_SOURCE_AGREEMENT_IDENTIFIER", + "MISSING_SOURCE_AGREEMENT_IDENTIFIER", + "INVALID_AGREEMENT_PROPOSAL_IDENTIFIER", + "MISSING_AGREEMENT_PROPOSAL_IDENTIFIER", + "INVALID_REQUESTED_TERMS", + "MISSING_REQUESTED_TERMS", + "INVALID_REQUESTED_TERM_ID", + "MISSING_REQUESTED_TERM_ID", + "INVALID_REQUESTED_TERM_CONFIGURATION", + "MISSING_REQUESTED_TERM_CONFIGURATION", + "INVALID_AGREEMENT_REQUEST_ID", + "MISSING_AGREEMENT_REQUEST_ID", + "INVALID_PURCHASE_ORDERS", + "MISSING_PURCHASE_ORDERS", + "INVALID_CHARGE_ID", + "MISSING_CHARGE_ID", + "INVALID_CHARGE_REVISION", + "MISSING_CHARGE_REVISION", + "INVALID_AGREEMENT_TYPE", + "INVALID_PURCHASE_ORDER_REFERENCE", + "INACTIVE_AGREEMENT", + "SUPERSEDED_AGREEMENT_PROPOSAL", + "EXPIRED_AGREEMENT_PROPOSAL", + "MISSING_MANDATORY_TERMS", + "INCOMPATIBLE_TERMS", + "MISSING_USAGE_AGREEMENT", + "INVALID_INCREMENTAL_CHARGE", + "MISSING_ACCOUNT_ADDRESS", + "UNSUPPORTED_ACTION", + "INVALID_REJECTION_REASON", + "INVALID_PAYMENT_REQUEST_STATUS", + "OTHER", + "DUPLICATE_CHARGES", + "UNSUPPORTED_ACCOUNT_PLAN", + "DUPLICATE_AGREEMENT_IN_ORGANIZATION", + "MISSING_PURCHASE_ORDER_REFERENCE", + ], + }, + }, +} + +_PAGINATOR_MODEL_PATCH = { + "GetAgreementEntitlements": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agreementEntitlements", + }, + "SearchAgreements": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "agreementViewSummaries", + }, +} + + +def apply_marketplace_agreement_compatibility(session): + # boto3 has no public API for amending a service model before client construction. + service_data = session._session.get_service_data("marketplace-agreement") + paginator_config = session._session.get_paginator_model( + "marketplace-agreement" + )._paginator_config + missing_operations = ( + _SERVICE_MODEL_PATCH["operations"].keys() + - service_data["operations"].keys() + ) + missing_paginators = _PAGINATOR_MODEL_PATCH.keys() - paginator_config.keys() + if not missing_operations and not missing_paginators: + return False + if missing_operations: + service_data["operations"].update( + { + name: _SERVICE_MODEL_PATCH["operations"][name] + for name in missing_operations + } + ) + for name, shape in _SERVICE_MODEL_PATCH["shapes"].items(): + service_data["shapes"].setdefault(name, shape) + paginator_config.update( + {name: _PAGINATOR_MODEL_PATCH[name] for name in missing_paginators} + ) + return True diff --git a/aws_quickstart/release.sh b/aws_quickstart/release.sh index 035358e0..7aeb3618 100755 --- a/aws_quickstart/release.sh +++ b/aws_quickstart/release.sh @@ -61,14 +61,21 @@ embed_python_source_with_common() { local template="$1" local source="$2" local placeholder="$3" + local supplemental_source="${4:-}" local composed_source composed_source=$(mktemp) - # CloudFormation ZipFile Lambdas are single-file modules, so inline the shared - # helper and remove the local-development import from the handler source. + # CloudFormation ZipFile Lambdas are single-file modules, so inline shared + # sources and remove their local-development imports from the handler source. { sed -e '$a\' cfn_common.py - sed '/^from cfn_common import send_cfn_response$/d' "$source" + if [ -n "${supplemental_source}" ]; then + sed -e '$a\' "${supplemental_source}" + fi + sed \ + -e '/^from cfn_common import send_cfn_response$/d' \ + -e '/^from marketplace_agreement_compat import apply_marketplace_agreement_compatibility$/d' \ + "$source" } > "${composed_source}" embed_python_source "$template" "${composed_source}" "$placeholder" rm -f "${composed_source}" @@ -156,6 +163,7 @@ trap "rm -rf ${TEMP_DIR}" EXIT # Copy all YAML files to temp directory cp *.yaml "${TEMP_DIR}/" cp datadog_agentless_api_call.py cfn_common.py attach_integration_permissions.py accept_operator_subscription.py "${TEMP_DIR}/" +cp marketplace_agreement_compat.py "${TEMP_DIR}/" # Change to temp directory for processing cd "${TEMP_DIR}" @@ -183,7 +191,7 @@ for template in main_workflow.yaml main_extended_workflow.yaml main_v2.yaml main done embed_python_source_with_common datadog_integration_permissions.yaml attach_integration_permissions.py ATTACH_INTEGRATION_PERMISSIONS_SOURCE -embed_python_source_with_common datadog_integration_permissions.yaml accept_operator_subscription.py ACCEPT_OPERATOR_SUBSCRIPTION_SOURCE +embed_python_source_with_common datadog_integration_permissions.yaml accept_operator_subscription.py ACCEPT_OPERATOR_SUBSCRIPTION_SOURCE marketplace_agreement_compat.py # Process Agentless Scanning templates for template in datadog_agentless_delegate_role.yaml datadog_agentless_scanning.yaml datadog_agentless_delegate_role_snapshot.yaml datadog_integration_autoscaling_policy.yaml datadog_integration_sds_policy.yaml datadog_agentless_delegate_role_stackset.yaml datadog_agentless_saas.yaml; do diff --git a/aws_quickstart/version.txt b/aws_quickstart/version.txt index b4abc501..9b56abd2 100644 --- a/aws_quickstart/version.txt +++ b/aws_quickstart/version.txt @@ -1 +1 @@ -v4.19.0 +v4.19.1