@opensoha/contracts 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPATIBILITY.md +91 -0
- package/README.md +49 -3
- package/auth/catalog_generated.go +2 -2
- package/auth/catalog_test.go +32 -0
- package/auth/outpost_headers.go +17 -0
- package/auth/outpost_headers_test.go +16 -0
- package/auth/permission-catalog.json +1097 -187
- package/compat/consumer-revisions.json +3 -0
- package/compat/json-schema-ecosystem-0.1.0.json +1 -1
- package/compat/openapi-loose-boundaries-0.1.0.json +78 -46
- package/delivery/canonical.go +136 -0
- package/delivery/document.go +203 -0
- package/delivery/document.schema.json +1527 -0
- package/delivery/document_test.go +191 -0
- package/delivery/parser.go +155 -0
- package/evaluation/evaluation-run.schema.json +64 -10
- package/fixtures/json-schema/gateway-manifest/invalid/bad-execution-mode.json +73 -0
- package/fixtures/json-schema/gateway-manifest/invalid/unversioned-check.json +85 -0
- package/fixtures/json-schema/gateway-manifest/valid/checks.json +86 -0
- package/fixtures/json-schema/gateway-manifest/valid/versioned-task.json +73 -0
- package/fixtures/json-schema/network-ingest-event/invalid/duplicate-event.json +33 -0
- package/fixtures/json-schema/network-ingest-event/invalid/message-payload-mismatch.json +23 -0
- package/fixtures/json-schema/network-ingest-event/invalid/password.json +22 -0
- package/fixtures/json-schema/network-ingest-event/invalid/proxy-flow-destination.json +28 -0
- package/fixtures/json-schema/network-ingest-event/invalid/vpn-probe-negative-rtt.json +34 -0
- package/fixtures/json-schema/network-ingest-event/invalid/vpn-stats-secret.json +27 -0
- package/fixtures/json-schema/network-ingest-event/valid/endpoint-proxy-flow.json +27 -0
- package/fixtures/json-schema/network-ingest-event/valid/freeradius-accounting.json +25 -0
- package/fixtures/json-schema/network-ingest-event/valid/gateway.json +53 -0
- package/fixtures/json-schema/network-ingest-event/valid/vpn-probe-batch.json +45 -0
- package/fixtures/json-schema/network-ingest-event/valid/vpn-tunnel-stats.json +26 -0
- package/fixtures/json-schema/network-radius-accounting/invalid/password.json +9 -0
- package/fixtures/json-schema/network-radius-accounting/valid/accounting.json +16 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/invalid-cidr.json +31 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/message-payload-mismatch.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/mihomo-raw-subscription.json +33 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/nas-authorization-password.json +18 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/nas-eap-without-certificate.json +17 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/private-key.json +20 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/raw-credential-reference.json +16 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/runtime-interval-too-short.json +23 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/vpn-managed-bad-token.json +15 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/vpn-managed-mode-override.json +16 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/wireguard-capability-without-key.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/wireguard-default-route.json +31 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/ztna-without-access-grant.json +17 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/configuration-desired.json +86 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/direct-ztna-connect.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/direct-ztna-result.json +33 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/internal-ztna-connect.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/managed-vpn-macos-enrollment.json +27 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-app-subscription-configuration.json +32 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-manual-node-configuration.json +32 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-wireguard-configuration.json +70 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/nas-authorization-request.json +19 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/nas-authorization-result.json +24 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/nas-session-command-result.json +17 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/nas-session-command.json +27 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/vpn-connect-request.json +17 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/vpn-connect-result.json +34 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/vpn-managed-connect.json +15 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/wireguard-enrollment.json +20 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/wireguard-site-link.json +48 -0
- package/fixtures/openapi/AgentChatInput/invalid/empty-question.json +4 -0
- package/fixtures/openapi/AgentChatInput/invalid/system-role.json +9 -0
- package/fixtures/openapi/AgentChatInput/valid/authorized-background.json +5 -0
- package/fixtures/openapi/AgentChatInput/valid/conversation.json +15 -0
- package/fixtures/openapi/AgentRunnerToolCallRequest/invalid/model-identity.json +1 -0
- package/fixtures/openapi/AgentRunnerToolCallRequest/valid/search.json +1 -0
- package/fixtures/openapi/AnnouncementReceiptPageEnvelope/invalid/missing-read-count.json +9 -0
- package/fixtures/openapi/AnnouncementReceiptPageEnvelope/valid/read-and-unread.json +28 -0
- package/fixtures/openapi/ApplicationInput/invalid/negative-version.json +6 -0
- package/fixtures/openapi/ApplicationInput/invalid/zero-version.json +6 -0
- package/fixtures/openapi/ApplicationInput/valid/versioned.json +6 -0
- package/fixtures/openapi/ApprovalRequest/invalid/resolved-scopes.json +20 -0
- package/fixtures/openapi/ApprovalRequest/valid/resolved-scopes.json +21 -0
- package/fixtures/openapi/BuildTemplateInput/invalid/negative-revision.json +6 -0
- package/fixtures/openapi/BuildTemplateInput/valid/draft.json +11 -0
- package/fixtures/openapi/BuildTriggerRequest/invalid/no-ref.json +1 -0
- package/fixtures/openapi/BuildTriggerRequest/valid/no-environment.json +1 -0
- package/fixtures/openapi/BuildpacksConfiguration/invalid/mutable-builder.json +5 -0
- package/fixtures/openapi/BuildpacksConfiguration/invalid/privilege-override.json +6 -0
- package/fixtures/openapi/BuildpacksConfiguration/invalid/unsupported-platform.json +5 -0
- package/fixtures/openapi/BuildpacksConfiguration/valid/pinned.json +5 -0
- package/fixtures/openapi/CapabilityAssessment/invalid/contract.json +16 -0
- package/fixtures/openapi/CapabilityAssessment/valid/contract.json +16 -0
- package/fixtures/openapi/CapabilityPlan/invalid/contract.json +16 -0
- package/fixtures/openapi/CapabilityPlan/valid/contract.json +19 -0
- package/fixtures/openapi/CapabilityPlanValidation/invalid/contract.json +5 -0
- package/fixtures/openapi/CapabilityPlanValidation/valid/contract.json +11 -0
- package/fixtures/openapi/CapabilityTask/invalid/contract.json +33 -0
- package/fixtures/openapi/CapabilityTask/valid/contract.json +34 -0
- package/fixtures/openapi/CapabilityTaskInput/invalid/contract.json +22 -0
- package/fixtures/openapi/CapabilityTaskInput/valid/contract.json +22 -0
- package/fixtures/openapi/CapabilityValueSemantic/invalid/contract.json +4 -0
- package/fixtures/openapi/CapabilityValueSemantic/valid/contract.json +7 -0
- package/fixtures/openapi/DeliveryAccessResult/invalid/release-verification.json +8 -0
- package/fixtures/openapi/DeliveryAccessResult/valid/release-verification.json +8 -0
- package/fixtures/openapi/DeliveryBatchAssessment/invalid/release-verification.json +11 -0
- package/fixtures/openapi/DeliveryBatchAssessment/valid/release-verification.json +11 -0
- package/fixtures/openapi/DeliveryBatchAssessmentInput/invalid/release-verification.json +9 -0
- package/fixtures/openapi/DeliveryBatchAssessmentInput/valid/release-verification.json +8 -0
- package/fixtures/openapi/DeliveryBatchInput/invalid/ambiguous-source.json +19 -0
- package/fixtures/openapi/DeliveryBatchInput/invalid/missing-idempotency.json +16 -0
- package/fixtures/openapi/DeliveryBatchInput/valid/quick-deploy.json +17 -0
- package/fixtures/openapi/DeliveryBatchInput/valid/saved-workflow.json +5 -0
- package/fixtures/openapi/DeliveryBlueprintInput/invalid/unversioned-preset.json +31 -0
- package/fixtures/openapi/DeliveryBlueprintInput/valid/service-preset.json +32 -0
- package/fixtures/openapi/DeliveryDocument/invalid/unknown-field.json +24 -0
- package/fixtures/openapi/DeliveryDocument/invalid/unknown-kind.json +23 -0
- package/fixtures/openapi/DeliveryDocument/invalid/unknown-version.json +23 -0
- package/fixtures/openapi/DeliveryDocument/valid/build-template.json +26 -0
- package/fixtures/openapi/DeliveryDocument/valid/deployment-template.json +26 -0
- package/fixtures/openapi/DeliveryDocument/valid/legacy-dag.json +39 -0
- package/fixtures/openapi/DeliveryDocument/valid/workflow-template.json +23 -0
- package/fixtures/openapi/DeliveryDocument/valid/workflow.json +20 -0
- package/fixtures/openapi/DeliveryDraftInput/invalid/short-idempotency-key.json +30 -0
- package/fixtures/openapi/DeliveryDraftInput/valid/idempotent-create.json +30 -0
- package/fixtures/openapi/DeliveryDraftService/invalid/zero-version.json +7 -0
- package/fixtures/openapi/DeliveryDraftService/valid/versioned-template.json +9 -0
- package/fixtures/openapi/DeliveryExecutionHistoryPage/invalid-kind.json +9 -0
- package/fixtures/openapi/DeliveryExecutionHistoryPage/valid-build.json +16 -0
- package/fixtures/openapi/DeliveryExecutionHistoryPage/valid-empty.json +3 -0
- package/fixtures/openapi/DeliveryPlanApprovalInput/invalid/confirm.json +1 -0
- package/fixtures/openapi/DeliveryPlanApprovalInput/valid/approve.json +1 -0
- package/fixtures/openapi/DeliveryPlanInput/invalid/draft-manifest-revision.json +1 -0
- package/fixtures/openapi/DeliveryPlanInput/valid/manifest-revision.json +1 -0
- package/fixtures/openapi/DeliveryTargetInput/invalid/deploy-without-environment.json +7 -0
- package/fixtures/openapi/DeliveryTargetInput/valid/build-without-environment.json +6 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/duplicate-kind.json +13 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/embedded-credentials.json +16 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/empty-kinds.json +10 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/negative-generation.json +15 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/unknown-kind.json +12 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/valid/manual-source.json +15 -0
- package/fixtures/openapi/DeliveryWorkflowDefinition/invalid/unversioned-template.json +15 -0
- package/fixtures/openapi/DeliveryWorkflowDefinition/valid/cross-app.json +24 -0
- package/fixtures/openapi/DeliveryWorkflowInput/invalid/short-key.json +1 -0
- package/fixtures/openapi/DeliveryWorkflowInput/valid/idempotent-create.json +1 -0
- package/fixtures/openapi/DeploymentTemplatePreviewInput/invalid/invalid-target-override.json +7 -0
- package/fixtures/openapi/DeploymentTemplatePreviewInput/invalid/invalid-zero-version.json +6 -0
- package/fixtures/openapi/DeploymentTemplatePreviewInput/valid/valid.json +6 -0
- package/fixtures/openapi/DockerDeliveryConfiguration/invalid/empty-mappings.json +5 -0
- package/fixtures/openapi/DockerDeliveryConfiguration/valid/compose.json +7 -0
- package/fixtures/openapi/DockerDeliverySnapshot/invalid/missing-required-field.json +19 -0
- package/fixtures/openapi/DockerDeliverySnapshot/valid/frozen-project.json +20 -0
- package/fixtures/openapi/DockerOperationCallbackRequest/invalid/cancellation-acknowledged-not-boolean.json +13 -0
- package/fixtures/openapi/DockerOperationCallbackRequest/valid/cancellation-acknowledged.json +13 -0
- package/fixtures/openapi/DockerProjectInput/invalid/empty-host.json +4 -0
- package/fixtures/openapi/DockerProjectInput/valid/inline-idempotent.json +7 -0
- package/fixtures/openapi/DockerQuickCreateHostInput/invalid/capacity-type.json +1 -0
- package/fixtures/openapi/DockerQuickCreateHostInput/valid/capacity.json +1 -0
- package/fixtures/openapi/EndpointDevice/invalid/missing-owner.json +8 -0
- package/fixtures/openapi/EndpointDevice/valid/active.json +35 -0
- package/fixtures/openapi/EndpointDeviceRegistrationInput/invalid/client-owned-status.json +5 -0
- package/fixtures/openapi/EndpointDeviceRegistrationInput/invalid/oversized-network-inventory.json +18 -0
- package/fixtures/openapi/EndpointDeviceRegistrationInput/valid/macos.json +28 -0
- package/fixtures/openapi/HelmDeliveryConfiguration/invalid/plain-credential.json +21 -0
- package/fixtures/openapi/HelmDeliveryConfiguration/invalid/release-name.json +20 -0
- package/fixtures/openapi/HelmDeliveryConfiguration/valid/private-oci.json +20 -0
- package/fixtures/openapi/HelmDeliverySnapshot/invalid/secret-plan.json +31 -0
- package/fixtures/openapi/HelmDeliverySnapshot/invalid/unknown-operation.json +28 -0
- package/fixtures/openapi/HelmDeliverySnapshot/valid/first-install.json +28 -0
- package/fixtures/openapi/IdentityApplication/invalid/outpost-status.json +1 -0
- package/fixtures/openapi/IdentityApplicationOnboardingInput/invalid/missing-access.json +12 -0
- package/fixtures/openapi/IdentityApplicationOnboardingInput/valid/link.json +13 -0
- package/fixtures/openapi/IdentityOutpost/invalid/missing-runtime-status.json +9 -0
- package/fixtures/openapi/IdentityOutpost/valid/healthy.json +17 -0
- package/fixtures/openapi/IdentityOutpost/valid/unregistered.json +12 -0
- package/fixtures/openapi/IdentityOutpostDeployment/invalid/missing-protocol.json +3 -0
- package/fixtures/openapi/IdentityOutpostDeployment/valid/remote.json +6 -0
- package/fixtures/openapi/IdentityOutpostInput/invalid/forged-observation.json +7 -0
- package/fixtures/openapi/IdentityOutpostInput/valid/forward-auth.json +6 -0
- package/fixtures/openapi/IdentityOutpostInput/valid/legacy.json +8 -0
- package/fixtures/openapi/IdentityProviderSetup/invalid/fake-verification.json +1 -0
- package/fixtures/openapi/IdentityProviderSetup/valid/incomplete.json +1 -0
- package/fixtures/openapi/IdentityProviderSetup/valid/remote.json +1 -0
- package/fixtures/openapi/IdentityProviderUserMetadata/invalid/attributes.json +8 -0
- package/fixtures/openapi/IdentityProviderUserMetadata/valid/attributes.json +13 -0
- package/fixtures/openapi/KubernetesCustomResource/invalid/negative-generation.json +1 -0
- package/fixtures/openapi/KubernetesCustomResource/valid/deleting-operator.json +1 -0
- package/fixtures/openapi/KubernetesResourceCreationReceipt/invalid/manifest-content.json +20 -0
- package/fixtures/openapi/KubernetesResourceCreationReceipt/valid/confirmed.json +20 -0
- package/fixtures/openapi/KubernetesResourceCreationReceipt/valid/unknown.json +19 -0
- package/fixtures/openapi/LLMUpstreamTestResultEnvelope/invalid/models-not-array.json +9 -0
- package/fixtures/openapi/LLMUpstreamTestResultEnvelope/valid/discovered-models.json +11 -0
- package/fixtures/openapi/ManifestDeploymentEnvelope/invalid/negative-resource-observation.json +57 -0
- package/fixtures/openapi/ManifestDeploymentEnvelope/valid/operator-deleting.json +57 -0
- package/fixtures/openapi/ManifestExecutionTaskPayload/invalid/gitops-empty.json +7 -0
- package/fixtures/openapi/ManifestExecutionTaskPayload/valid/gitops-frozen-children.json +18 -0
- package/fixtures/openapi/ManifestKustomizeOptions/invalid/escaping-entry.json +1 -0
- package/fixtures/openapi/ManifestKustomizeOptions/invalid/mutable-image.json +1 -0
- package/fixtures/openapi/ManifestKustomizeOptions/valid/overlay-images.json +7 -0
- package/fixtures/openapi/ManifestRevisionInput/invalid/missing-version.json +1 -0
- package/fixtures/openapi/ManifestRevisionInput/valid/current-draft.json +1 -0
- package/fixtures/openapi/ManifestServiceTemplateInputs/invalid/mutable-image.json +8 -0
- package/fixtures/openapi/ManifestServiceTemplateInputs/valid/frozen-service-image.json +9 -0
- package/fixtures/openapi/NetworkAccessGrant/invalid/token-digest.json +15 -0
- package/fixtures/openapi/NetworkAccessGrant/valid/issued.json +14 -0
- package/fixtures/openapi/NetworkAccessGrantInput/invalid/no-resources.json +7 -0
- package/fixtures/openapi/NetworkAccessGrantInput/valid/direct-ztna.json +8 -0
- package/fixtures/openapi/NetworkAccessGrantSecret/invalid/short-token.json +17 -0
- package/fixtures/openapi/NetworkAccessGrantSecret/valid/issued.json +17 -0
- package/fixtures/openapi/NetworkAccessPolicy/invalid/missing-version.json +20 -0
- package/fixtures/openapi/NetworkAccessPolicy/valid/published-draft.json +21 -0
- package/fixtures/openapi/NetworkAccessPolicyInput/invalid/duplicate-team.json +17 -0
- package/fixtures/openapi/NetworkAccessPolicyInput/valid/team-and-tag-allow.json +17 -0
- package/fixtures/openapi/NetworkConflictAnalysisRequest/invalid/ipv6-range.json +9 -0
- package/fixtures/openapi/NetworkConflictAnalysisRequest/valid/runtime-ranges.json +15 -0
- package/fixtures/openapi/NetworkConnectionOption/invalid/secret.json +10 -0
- package/fixtures/openapi/NetworkConnectionOption/valid/wifi.json +9 -0
- package/fixtures/openapi/NetworkConnectionOption/valid/wired.json +8 -0
- package/fixtures/openapi/NetworkGateway/invalid/missing-site.json +7 -0
- package/fixtures/openapi/NetworkGateway/valid/online.json +23 -0
- package/fixtures/openapi/NetworkGatewayInput/invalid/default-route-overlay.json +13 -0
- package/fixtures/openapi/NetworkGatewayInput/valid/routed.json +14 -0
- package/fixtures/openapi/NetworkLease/invalid/empty-cidrs.json +12 -0
- package/fixtures/openapi/NetworkLease/valid/active.json +12 -0
- package/fixtures/openapi/NetworkMihomoProfile/invalid/subscription-leak.json +20 -0
- package/fixtures/openapi/NetworkMihomoProfile/valid/managed.json +21 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/invalid/app-subscription-secret.json +14 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/invalid/manual-node-with-subscription.json +20 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/valid/app-subscription.json +14 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/valid/managed.json +16 -0
- package/fixtures/openapi/NetworkMihomoProfileInput/valid/manual-node.json +21 -0
- package/fixtures/openapi/NetworkNASBinding/invalid/secret.json +13 -0
- package/fixtures/openapi/NetworkNASBinding/valid/default.json +16 -0
- package/fixtures/openapi/NetworkNASBindingInput/invalid/secret.json +10 -0
- package/fixtures/openapi/NetworkNASBindingInput/valid/default.json +13 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/invalid/unknown-mode.json +6 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/external-direct-ztna.json +6 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/external-vpn-ztna.json +6 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/external-vpn.json +6 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/internal-direct.json +7 -0
- package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/internal-ztna.json +7 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/invalid/bypass-path.json +9 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/onboarding.json +10 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/protected-broad-path-denied.json +9 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/protected-resource.json +9 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/quarantine.json +10 -0
- package/fixtures/openapi/NetworkPolicyPreviewResult/valid/restricted.json +10 -0
- package/fixtures/openapi/NetworkPolicySnapshot/invalid/zero-version.json +7 -0
- package/fixtures/openapi/NetworkPolicySnapshot/valid/published.json +7 -0
- package/fixtures/openapi/NetworkResourceInput/invalid/invalid-port.json +10 -0
- package/fixtures/openapi/NetworkResourceInput/valid/protected-cidr.json +10 -0
- package/fixtures/openapi/NetworkRuntimeEnrollmentInput/invalid/excessive-ttl.json +7 -0
- package/fixtures/openapi/NetworkRuntimeEnrollmentInput/valid/endpoint.json +7 -0
- package/fixtures/openapi/NetworkRuntimeEnrollmentSecret/invalid/short-token.json +15 -0
- package/fixtures/openapi/NetworkRuntimeEnrollmentSecret/valid/pending.json +15 -0
- package/fixtures/openapi/NetworkSession/invalid/unknown-status.json +14 -0
- package/fixtures/openapi/NetworkSession/valid/restricted-vpn-ztna.json +16 -0
- package/fixtures/openapi/NetworkSessionActionExecuteInput/invalid/short-hash.json +6 -0
- package/fixtures/openapi/NetworkSessionActionExecuteInput/valid/restrict.json +6 -0
- package/fixtures/openapi/NetworkSessionActionInput/invalid/unknown-action.json +5 -0
- package/fixtures/openapi/NetworkSessionActionInput/valid/restrict.json +5 -0
- package/fixtures/openapi/NetworkSessionActionPlan/invalid/missing-hash.json +12 -0
- package/fixtures/openapi/NetworkSessionActionPlan/valid/fallback-disconnect.json +13 -0
- package/fixtures/openapi/NetworkSessionCommand/invalid/unknown-status.json +14 -0
- package/fixtures/openapi/NetworkSessionCommand/valid/pending.json +14 -0
- package/fixtures/openapi/NetworkSiteInput/invalid/unknown-status.json +4 -0
- package/fixtures/openapi/NetworkSiteInput/valid/shanghai.json +5 -0
- package/fixtures/openapi/NetworkSiteProfileBinding/invalid/vlan.json +9 -0
- package/fixtures/openapi/NetworkSiteProfileBinding/valid/default.json +10 -0
- package/fixtures/openapi/NetworkSiteProfileBindingInput/invalid/timeout.json +7 -0
- package/fixtures/openapi/NetworkSiteProfileBindingInput/valid/default.json +7 -0
- package/fixtures/openapi/NetworkSpaceInput/invalid/empty-cidrs.json +6 -0
- package/fixtures/openapi/NetworkSpaceInput/invalid/malformed-cidr.json +6 -0
- package/fixtures/openapi/NetworkSpaceInput/valid/corp.json +6 -0
- package/fixtures/openapi/NetworkTelemetrySummary/invalid/raw-destination.json +29 -0
- package/fixtures/openapi/NetworkTelemetrySummary/valid/proxy-flow.json +36 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/auto-with-gateway.json +6 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/missing-manual-gateway.json +5 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/mode-override.json +7 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/valid/auto-intent.json +5 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/valid/manual-intent.json +6 -0
- package/fixtures/openapi/NetworkVPNProfileInput/invalid/unknown-private-key.json +25 -0
- package/fixtures/openapi/NetworkVPNProfileInput/valid/assigned-profile.json +24 -0
- package/fixtures/openapi/NetworkVPNSelectionPolicyInput/invalid/unbounded-attempts.json +21 -0
- package/fixtures/openapi/NetworkVPNSelectionPolicyInput/valid/provider-policy.json +21 -0
- package/fixtures/openapi/ObservabilityMetricAssessmentInput/invalid/arbitrary-expression.json +10 -0
- package/fixtures/openapi/ObservabilityMetricAssessmentInput/valid/metric-window.json +10 -0
- package/fixtures/openapi/PluginManifest/valid/hermes-api.json +53 -0
- package/fixtures/openapi/ProgressiveRolloutControlInput/invalid/missing-identity.json +5 -0
- package/fixtures/openapi/ProgressiveRolloutControlInput/invalid/skip-analysis.json +5 -0
- package/fixtures/openapi/ProgressiveRolloutControlInput/valid/pause.json +5 -0
- package/fixtures/openapi/ReleaseTargetInput/invalid/missing-required-field.json +15 -0
- package/fixtures/openapi/ReleaseTargetInput/valid/docker-compose.json +16 -0
- package/fixtures/openapi/RepositoryAnalysis/invalid/moving-ref.json +11 -0
- package/fixtures/openapi/RepositoryAnalysis/valid/go.json +29 -0
- package/fixtures/openapi/RepositoryAnalysisInput/invalid/arbitrary-source-url.json +6 -0
- package/fixtures/openapi/RepositoryAnalysisInput/invalid/missing-ref.json +4 -0
- package/fixtures/openapi/RepositoryAnalysisInput/valid/nested-project.json +6 -0
- package/fixtures/openapi/ResourceLease/invalid/missing-session.json +10 -0
- package/fixtures/openapi/ResourceLease/valid/active.json +11 -0
- package/fixtures/openapi/ServiceDeploymentTemplateBinding/invalid/invalid-unpinned.json +4 -0
- package/fixtures/openapi/ServiceDeploymentTemplateBinding/valid/valid.json +7 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-executable-schema.json +25 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-missing-schema.json +21 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-mixed-renderers.json +40 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/valid/valid-nested-parameters.json +41 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/valid/valid-yaml.json +34 -0
- package/fixtures/openapi/TemplatePublishInput/invalid/missing-revision.json +1 -0
- package/fixtures/openapi/TemplatePublishInput/invalid/unknown-field.json +4 -0
- package/fixtures/openapi/TemplatePublishInput/invalid/zero-revision.json +3 -0
- package/fixtures/openapi/TemplatePublishInput/valid/revision.json +3 -0
- package/fixtures/openapi/ToolCapability/invalid/bad-idempotency.json +35 -0
- package/fixtures/openapi/ToolCapability/invalid/unversioned-check.json +47 -0
- package/fixtures/openapi/ToolCapability/valid/checks.json +48 -0
- package/fixtures/openapi/ToolCapability/valid/versioned-task.json +35 -0
- package/fixtures/openapi/ToolInvocationRequest/invalid/empty-version.json +8 -0
- package/fixtures/openapi/ToolInvocationRequest/valid/versioned.json +8 -0
- package/fixtures/openapi/ToolInvocationResult/invalid/missing-status-call.json +13 -0
- package/fixtures/openapi/ToolInvocationResult/valid/task.json +20 -0
- package/fixtures/openapi/VirtualMachineCreateInput/invalid/capacity-type.json +1 -0
- package/fixtures/openapi/VirtualMachineCreateInput/valid/capacity.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityInput/invalid/missing-disk.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityInput/valid/running-vm.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityResult/invalid/invented-status.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityResult/valid/available.json +1 -0
- package/fixtures/openapi/VirtualizationWorkerCreateInput/invalid/secret-input.json +5 -0
- package/fixtures/openapi/VirtualizationWorkerCreateInput/valid/one-worker.json +4 -0
- package/fixtures/openapi/VirtualizationWorkerPoolInput/invalid/external-owner.json +27 -0
- package/fixtures/openapi/VirtualizationWorkerPoolInput/invalid/zero-budget.json +27 -0
- package/fixtures/openapi/VirtualizationWorkerPoolInput/valid/registered.json +27 -0
- package/fixtures/openapi/VirtualizationWorkerPoolListEnvelope/invalid/nested-data.json +5 -0
- package/fixtures/openapi/VirtualizationWorkerPoolListEnvelope/valid/empty.json +3 -0
- package/fixtures/openapi/WorkbenchContextSelection/invalid/missing-file-content.json +8 -0
- package/fixtures/openapi/WorkbenchContextSelection/invalid/unknown-reference-kind.json +8 -0
- package/fixtures/openapi/WorkbenchContextSelection/valid/text-and-resource.json +17 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/trigger-without-plan.json +10 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/unknown-trigger.json +28 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/zero-interval.json +28 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/valid/alert-capability.json +30 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/valid/scheduled-capability.json +28 -0
- package/fixtures/openapi/WorkbenchInspectionTrigger/invalid/missing-rule.json +3 -0
- package/fixtures/openapi/WorkbenchInspectionTrigger/invalid/schedule-alert-selector.json +4 -0
- package/fixtures/openapi/WorkbenchInspectionTrigger/valid/schedule.json +3 -0
- package/fixtures/openapi/WorkbenchModelPreferences/invalid/unknown-effort.json +1 -0
- package/fixtures/openapi/WorkbenchModelPreferences/valid/selected.json +1 -0
- package/fixtures/openapi/WorkflowCatalogPage/invalid/unsupported-kind.json +6 -0
- package/fixtures/openapi/WorkflowCatalogPage/valid/build-without-environment.json +6 -0
- package/fixtures/openapi/WorkflowTemplateInput/invalid/zero-revision.json +6 -0
- package/fixtures/openapi/WorkflowTemplateInput/valid/draft.json +10 -0
- package/gen/go/sohaapi/client.go +1 -1
- package/gen/go/sohaapi/types.go +15610 -6415
- package/gen/go/sohaapi/types_test.go +100 -1
- package/gen/go/sohaapi/vpn_models_test.go +21 -0
- package/gen/ts/sohaapi/index.ts +27859 -17518
- package/go.mod +107 -5
- package/go.sum +379 -16
- package/helmrelease/delivery.go +283 -0
- package/helmrelease/delivery_test.go +102 -0
- package/helmrelease/frozen_chart.go +91 -0
- package/helmrelease/result.go +27 -0
- package/helmrelease/runtime/delivery.go +508 -0
- package/helmrelease/runtime/delivery_test.go +236 -0
- package/helmrelease/runtime/resources_test.go +60 -0
- package/helmrelease/values.go +99 -0
- package/knowledge/context-envelope.schema.json +1 -0
- package/mcp/gateway-manifest.schema.json +387 -72
- package/network/README.md +156 -0
- package/network/network-ingest-event.schema.json +415 -0
- package/network/network-radius-accounting.schema.json +41 -0
- package/network/network-runtime-protocol.schema.json +1208 -0
- package/network/schema.go +26 -0
- package/network/schema_test.go +22 -0
- package/openapi/soha-api.json +59260 -35960
- package/openapi/soha-api.yaml +36064 -26232
- package/openapi/spec_generated.go +3 -3
- package/package.json +7 -1
- package/resource/manifest_health.go +155 -0
- package/resource/manifest_health_test.go +37 -0
- package/resource/models.go +14 -0
- package/resource/runtime/argocd.go +166 -0
- package/resource/runtime/argocd_integration_test.go +142 -0
- package/resource/runtime/argocd_observation.go +143 -0
- package/resource/runtime/argocd_operation.go +253 -0
- package/resource/runtime/argocd_resources.go +115 -0
- package/resource/runtime/argocd_resources_test.go +107 -0
- package/resource/runtime/argocd_test.go +386 -0
- package/resource/runtime/health.go +115 -0
- package/resource/runtime/health_test.go +78 -0
- package/resource/runtime/operator_integration_test.go +174 -0
- package/resource/runtime/ownership.go +178 -0
- package/resource/runtime/ownership_test.go +178 -0
- package/resource/runtime/rollout_integration_test.go +287 -0
- package/resource/runtime/rollout_observation.go +252 -0
- package/resource/runtime/rollout_operation.go +398 -0
- package/resource/runtime/rollout_resources.go +385 -0
- package/resource/runtime/rollout_task.go +165 -0
- package/resource/runtime/rollout_test.go +450 -0
|
@@ -5,9 +5,9 @@ package openapi
|
|
|
5
5
|
import _ "embed"
|
|
6
6
|
|
|
7
7
|
const (
|
|
8
|
-
Version = "0.1.
|
|
9
|
-
YAMLSHA256 = "
|
|
10
|
-
JSONSHA256 = "
|
|
8
|
+
Version = "0.1.18"
|
|
9
|
+
YAMLSHA256 = "d614ce858924d94da353c7a9380971a3e80964f3e6c865e8b27108e859500cf4"
|
|
10
|
+
JSONSHA256 = "a98ea9ad99b6f044ce14e42f37b90b42e28347d53e37c727a04210b076aba960"
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
//go:embed soha-api.yaml
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensoha/contracts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared OpenSoha API, agent, MCP, skill, event, and auth contracts.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"test": "npm run check:version-sync && npm run check:generated && npm run validate && npm run lint:openapi && npm run check:openapi-breaking && npm run check:openapi-loose-boundaries && npm run check:json-schema-compat && npm run typecheck && go test ./..."
|
|
29
29
|
},
|
|
30
30
|
"exports": {
|
|
31
|
+
"./delivery/document.schema.json": "./delivery/document.schema.json",
|
|
31
32
|
"./gen/ts/sohaapi": "./gen/ts/sohaapi/index.ts",
|
|
32
33
|
"./openapi/soha-api.yaml": "./openapi/soha-api.yaml",
|
|
33
34
|
"./openapi/soha-api.json": "./openapi/soha-api.json",
|
|
@@ -61,6 +62,9 @@
|
|
|
61
62
|
"./auth/permission-catalog.json": "./auth/permission-catalog.json",
|
|
62
63
|
"./auth/permission-catalog.schema.json": "./auth/permission-catalog.schema.json",
|
|
63
64
|
"./connectors/connector-event-envelope.schema.json": "./connectors/connector-event-envelope.schema.json",
|
|
65
|
+
"./network/network-runtime-protocol.schema.json": "./network/network-runtime-protocol.schema.json",
|
|
66
|
+
"./network/network-ingest-event.schema.json": "./network/network-ingest-event.schema.json",
|
|
67
|
+
"./network/network-radius-accounting.schema.json": "./network/network-radius-accounting.schema.json",
|
|
64
68
|
"./knowledge/knowledge-base.schema.json": "./knowledge/knowledge-base.schema.json",
|
|
65
69
|
"./knowledge/knowledge-source.schema.json": "./knowledge/knowledge-source.schema.json",
|
|
66
70
|
"./knowledge/knowledge-search.schema.json": "./knowledge/knowledge-search.schema.json",
|
|
@@ -87,6 +91,7 @@
|
|
|
87
91
|
"./memory/memory-policy.schema.json": "./memory/memory-policy.schema.json"
|
|
88
92
|
},
|
|
89
93
|
"files": [
|
|
94
|
+
"delivery",
|
|
90
95
|
"COMPATIBILITY.md",
|
|
91
96
|
"go.mod",
|
|
92
97
|
"go.sum",
|
|
@@ -112,6 +117,7 @@
|
|
|
112
117
|
"events",
|
|
113
118
|
"auth",
|
|
114
119
|
"connectors",
|
|
120
|
+
"network",
|
|
115
121
|
"knowledge",
|
|
116
122
|
"memory"
|
|
117
123
|
],
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
package resource
|
|
2
|
+
|
|
3
|
+
import "encoding/json"
|
|
4
|
+
|
|
5
|
+
// ManifestHealth gives Direct and Agent execution the same conservative health
|
|
6
|
+
// interpretation. Successful persistence alone is health evidence only for
|
|
7
|
+
// built-in configuration objects, never for an unknown custom resource.
|
|
8
|
+
func ManifestHealth(object map[string]any) string {
|
|
9
|
+
if object == nil {
|
|
10
|
+
return "unknown"
|
|
11
|
+
}
|
|
12
|
+
encoded, err := json.Marshal(object)
|
|
13
|
+
if err != nil {
|
|
14
|
+
return "unknown"
|
|
15
|
+
}
|
|
16
|
+
var item manifestHealthObject
|
|
17
|
+
if err := json.Unmarshal(encoded, &item); err != nil {
|
|
18
|
+
return "unknown"
|
|
19
|
+
}
|
|
20
|
+
if item.Metadata.DeletionTimestamp != "" {
|
|
21
|
+
return "degraded"
|
|
22
|
+
}
|
|
23
|
+
kind := item.APIVersion + "/" + item.Kind
|
|
24
|
+
switch kind {
|
|
25
|
+
case "workloads.soha.io/v1alpha1/WorkloadCronJob":
|
|
26
|
+
// Ready describes configuration synchronization. Its source and owned
|
|
27
|
+
// CronJob must also be observed by the runtime before declaring health.
|
|
28
|
+
if item.conditionHealth(true) == "degraded" {
|
|
29
|
+
return "degraded"
|
|
30
|
+
}
|
|
31
|
+
return "unknown"
|
|
32
|
+
case "v1/ConfigMap", "v1/Secret", "v1/ServiceAccount", "rbac.authorization.k8s.io/v1/Role", "rbac.authorization.k8s.io/v1/RoleBinding", "networking.k8s.io/v1/NetworkPolicy":
|
|
33
|
+
return "healthy"
|
|
34
|
+
case "apps/v1/Deployment", "apps/v1/StatefulSet", "apps/v1/DaemonSet":
|
|
35
|
+
return item.workloadHealth()
|
|
36
|
+
case "v1/PersistentVolumeClaim":
|
|
37
|
+
if item.Status.Phase == "Bound" {
|
|
38
|
+
return "healthy"
|
|
39
|
+
}
|
|
40
|
+
if item.Status.Phase == "Lost" {
|
|
41
|
+
return "degraded"
|
|
42
|
+
}
|
|
43
|
+
return "progressing"
|
|
44
|
+
case "v1/Service":
|
|
45
|
+
if item.Spec.Type != "LoadBalancer" || len(item.Status.LoadBalancer.Ingress) > 0 {
|
|
46
|
+
return "healthy"
|
|
47
|
+
}
|
|
48
|
+
return "progressing"
|
|
49
|
+
case "v1/Pod", "batch/v1/Job":
|
|
50
|
+
if item.Status.Phase == "Succeeded" {
|
|
51
|
+
return "healthy"
|
|
52
|
+
}
|
|
53
|
+
if item.Status.Phase == "Failed" {
|
|
54
|
+
return "degraded"
|
|
55
|
+
}
|
|
56
|
+
return item.conditionHealth(false)
|
|
57
|
+
default:
|
|
58
|
+
return item.conditionHealth(true)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type manifestHealthObject struct {
|
|
63
|
+
APIVersion string `json:"apiVersion"`
|
|
64
|
+
Kind string `json:"kind"`
|
|
65
|
+
Metadata struct {
|
|
66
|
+
Generation int64 `json:"generation"`
|
|
67
|
+
DeletionTimestamp string `json:"deletionTimestamp"`
|
|
68
|
+
} `json:"metadata"`
|
|
69
|
+
Spec struct {
|
|
70
|
+
Replicas *int64 `json:"replicas"`
|
|
71
|
+
Type string `json:"type"`
|
|
72
|
+
UpdateStrategy struct {
|
|
73
|
+
RollingUpdate struct {
|
|
74
|
+
Partition int64 `json:"partition"`
|
|
75
|
+
} `json:"rollingUpdate"`
|
|
76
|
+
} `json:"updateStrategy"`
|
|
77
|
+
} `json:"spec"`
|
|
78
|
+
Status struct {
|
|
79
|
+
ObservedGeneration int64 `json:"observedGeneration"`
|
|
80
|
+
Replicas int64 `json:"replicas"`
|
|
81
|
+
ReadyReplicas int64 `json:"readyReplicas"`
|
|
82
|
+
UpdatedReplicas int64 `json:"updatedReplicas"`
|
|
83
|
+
AvailableReplicas int64 `json:"availableReplicas"`
|
|
84
|
+
DesiredNumberScheduled int64 `json:"desiredNumberScheduled"`
|
|
85
|
+
UpdatedNumberScheduled int64 `json:"updatedNumberScheduled"`
|
|
86
|
+
NumberAvailable int64 `json:"numberAvailable"`
|
|
87
|
+
Phase string `json:"phase"`
|
|
88
|
+
LoadBalancer struct {
|
|
89
|
+
Ingress []json.RawMessage `json:"ingress"`
|
|
90
|
+
} `json:"loadBalancer"`
|
|
91
|
+
Conditions []struct {
|
|
92
|
+
Type string `json:"type"`
|
|
93
|
+
Status string `json:"status"`
|
|
94
|
+
Reason string `json:"reason"`
|
|
95
|
+
ObservedGeneration int64 `json:"observedGeneration"`
|
|
96
|
+
} `json:"conditions"`
|
|
97
|
+
} `json:"status"`
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
func (item manifestHealthObject) workloadHealth() string {
|
|
101
|
+
if item.Metadata.Generation < 1 || item.Status.ObservedGeneration < item.Metadata.Generation {
|
|
102
|
+
return "progressing"
|
|
103
|
+
}
|
|
104
|
+
if item.conditionHealth(false) == "degraded" {
|
|
105
|
+
return "degraded"
|
|
106
|
+
}
|
|
107
|
+
desired := int64(1)
|
|
108
|
+
if item.Spec.Replicas != nil {
|
|
109
|
+
desired = *item.Spec.Replicas
|
|
110
|
+
}
|
|
111
|
+
healthy := false
|
|
112
|
+
switch item.Kind {
|
|
113
|
+
case "Deployment":
|
|
114
|
+
healthy = item.Status.UpdatedReplicas == desired && item.Status.Replicas == desired && item.Status.AvailableReplicas >= desired
|
|
115
|
+
case "StatefulSet":
|
|
116
|
+
updated := max(int64(0), desired-item.Spec.UpdateStrategy.RollingUpdate.Partition)
|
|
117
|
+
healthy = item.Status.ReadyReplicas >= desired && item.Status.UpdatedReplicas >= updated
|
|
118
|
+
case "DaemonSet":
|
|
119
|
+
healthy = item.Status.UpdatedNumberScheduled == item.Status.DesiredNumberScheduled && item.Status.NumberAvailable >= item.Status.DesiredNumberScheduled
|
|
120
|
+
}
|
|
121
|
+
if healthy {
|
|
122
|
+
return "healthy"
|
|
123
|
+
}
|
|
124
|
+
return "progressing"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func (item manifestHealthObject) conditionHealth(requireCurrentGeneration bool) string {
|
|
128
|
+
result := "progressing"
|
|
129
|
+
notReady := false
|
|
130
|
+
if requireCurrentGeneration {
|
|
131
|
+
result = "unknown"
|
|
132
|
+
}
|
|
133
|
+
for _, condition := range item.Status.Conditions {
|
|
134
|
+
if requireCurrentGeneration && (item.Metadata.Generation < 1 || max(condition.ObservedGeneration, item.Status.ObservedGeneration) < item.Metadata.Generation) {
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
if condition.Status == "True" && (condition.Type == "Failed" || condition.Type == "Degraded" || condition.Type == "ReplicaFailure") {
|
|
138
|
+
return "degraded"
|
|
139
|
+
}
|
|
140
|
+
if condition.Type == "Progressing" && condition.Status == "False" && condition.Reason == "ProgressDeadlineExceeded" {
|
|
141
|
+
return "degraded"
|
|
142
|
+
}
|
|
143
|
+
if condition.Type == "Ready" || condition.Type == "Available" || condition.Type == "Complete" || condition.Type == "Established" {
|
|
144
|
+
if condition.Status == "True" {
|
|
145
|
+
result = "healthy"
|
|
146
|
+
} else {
|
|
147
|
+
notReady = true
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if notReady {
|
|
152
|
+
return "progressing"
|
|
153
|
+
}
|
|
154
|
+
return result
|
|
155
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
package resource
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
func TestManifestHealth(t *testing.T) {
|
|
9
|
+
for _, test := range []struct{ name, object, want string }{
|
|
10
|
+
{"configuration", `{"apiVersion":"v1","kind":"ConfigMap"}`, "healthy"},
|
|
11
|
+
{"unknown CR", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2}}`, "unknown"},
|
|
12
|
+
{"custom kind cannot impersonate built-in", `{"apiVersion":"example.com/v1","kind":"ConfigMap"}`, "unknown"},
|
|
13
|
+
{"unobserved generation", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"spec":{"replicas":1},"status":{"observedGeneration":1,"replicas":1,"updatedReplicas":1,"availableReplicas":1,"conditions":[{"type":"Available","status":"True"}]}}`, "progressing"},
|
|
14
|
+
{"old replicas still available", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"spec":{"replicas":1},"status":{"observedGeneration":2,"replicas":2,"updatedReplicas":1,"availableReplicas":2}}`, "progressing"},
|
|
15
|
+
{"available but not updated", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"spec":{"replicas":1},"status":{"observedGeneration":2,"replicas":1,"updatedReplicas":0,"availableReplicas":1}}`, "progressing"},
|
|
16
|
+
{"deployment converged", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"spec":{"replicas":1},"status":{"observedGeneration":2,"replicas":1,"updatedReplicas":1,"availableReplicas":1}}`, "healthy"},
|
|
17
|
+
{"deployment failed after available", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"status":{"observedGeneration":2,"replicas":1,"updatedReplicas":1,"availableReplicas":1,"conditions":[{"type":"Available","status":"True"},{"type":"Progressing","status":"False","reason":"ProgressDeadlineExceeded"}]}}`, "degraded"},
|
|
18
|
+
{"stale custom ready", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2},"status":{"conditions":[{"type":"Ready","status":"True","observedGeneration":1}]}}`, "unknown"},
|
|
19
|
+
{"current custom ready", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2},"status":{"conditions":[{"type":"Ready","status":"True","observedGeneration":2}]}}`, "healthy"},
|
|
20
|
+
{"conflicting current ready conditions", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2},"status":{"observedGeneration":2,"conditions":[{"type":"Available","status":"True"},{"type":"Ready","status":"False"}]}}`, "progressing"},
|
|
21
|
+
{"current custom failure", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2},"status":{"observedGeneration":2,"conditions":[{"type":"Ready","status":"True"},{"type":"Failed","status":"True"}]}}`, "degraded"},
|
|
22
|
+
{"deleting object", `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"deletionTimestamp":"2026-09-12T00:00:00Z"}}`, "degraded"},
|
|
23
|
+
{"pod not ready", `{"apiVersion":"v1","kind":"Pod","status":{"phase":"Running","conditions":[{"type":"Ready","status":"False"}]}}`, "progressing"},
|
|
24
|
+
{"statefulset partition", `{"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"generation":2},"spec":{"replicas":3,"updateStrategy":{"rollingUpdate":{"partition":2}}},"status":{"observedGeneration":2,"readyReplicas":3,"updatedReplicas":1}}`, "healthy"},
|
|
25
|
+
{"daemonset rolling", `{"apiVersion":"apps/v1","kind":"DaemonSet","metadata":{"generation":2},"status":{"observedGeneration":2,"desiredNumberScheduled":3,"updatedNumberScheduled":2,"numberAvailable":3}}`, "progressing"},
|
|
26
|
+
} {
|
|
27
|
+
t.Run(test.name, func(t *testing.T) {
|
|
28
|
+
var object map[string]any
|
|
29
|
+
if err := json.Unmarshal([]byte(test.object), &object); err != nil {
|
|
30
|
+
t.Fatal(err)
|
|
31
|
+
}
|
|
32
|
+
if got := ManifestHealth(object); got != test.want {
|
|
33
|
+
t.Fatalf("health = %s, want %s", got, test.want)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
}
|
package/resource/models.go
CHANGED
|
@@ -1097,6 +1097,10 @@ type CRDView struct {
|
|
|
1097
1097
|
}
|
|
1098
1098
|
|
|
1099
1099
|
type CustomResourceView struct {
|
|
1100
|
+
UID string `json:"uid,omitempty"`
|
|
1101
|
+
Generation int64 `json:"generation,omitempty"`
|
|
1102
|
+
DeletingAt string `json:"deletingAt,omitempty"`
|
|
1103
|
+
Finalizers []string `json:"finalizers,omitempty"`
|
|
1100
1104
|
APIVersion string `json:"apiVersion,omitempty"`
|
|
1101
1105
|
Kind string `json:"kind"`
|
|
1102
1106
|
Name string `json:"name"`
|
|
@@ -1107,6 +1111,16 @@ type CustomResourceView struct {
|
|
|
1107
1111
|
AllowedActions []string `json:"allowedActions,omitempty"`
|
|
1108
1112
|
}
|
|
1109
1113
|
|
|
1114
|
+
// CustomResourceYAMLRequest is the Core/Agent custom-resource transport.
|
|
1115
|
+
// ExpectedUID fences deletion against replacement after a user viewed the object.
|
|
1116
|
+
type CustomResourceYAMLRequest struct {
|
|
1117
|
+
Definition CRDResourceDefinition `json:"definition"`
|
|
1118
|
+
Namespace string `json:"namespace"`
|
|
1119
|
+
Name string `json:"name,omitempty"`
|
|
1120
|
+
Content string `json:"content,omitempty"`
|
|
1121
|
+
ExpectedUID string `json:"expectedUid,omitempty"`
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1110
1124
|
type HelmReleaseView struct {
|
|
1111
1125
|
Name string `json:"name"`
|
|
1112
1126
|
Namespace string `json:"namespace"`
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
package runtime
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/hex"
|
|
5
|
+
"fmt"
|
|
6
|
+
"net/url"
|
|
7
|
+
"path"
|
|
8
|
+
"reflect"
|
|
9
|
+
"regexp"
|
|
10
|
+
"slices"
|
|
11
|
+
"strings"
|
|
12
|
+
|
|
13
|
+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
14
|
+
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const (
|
|
18
|
+
ArgoOwnerAnnotation = "delivery.soha.io/argocd-owner"
|
|
19
|
+
ArgoOperationAnnotation = "delivery.soha.io/argocd-operation"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
var argoApplications = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
|
|
23
|
+
var argoImageOverride = regexp.MustCompile(`^(?:([A-Za-z0-9][A-Za-z0-9._:/-]*)=)?([A-Za-z0-9][A-Za-z0-9._:/-]*)@sha256:[a-f0-9]{64}$`)
|
|
24
|
+
|
|
25
|
+
// ParseArgoImageOverride accepts Kustomize's image@digest and name=image@digest forms.
|
|
26
|
+
func ParseArgoImageOverride(value string) (name, image string, err error) {
|
|
27
|
+
parts := argoImageOverride.FindStringSubmatch(value)
|
|
28
|
+
if len(parts) != 3 || strings.LastIndex(parts[2], ":") > strings.LastIndex(parts[2], "/") {
|
|
29
|
+
return "", "", fmt.Errorf("GitOps image overrides require an immutable image digest without a tag")
|
|
30
|
+
}
|
|
31
|
+
name = parts[1]
|
|
32
|
+
if name == "" {
|
|
33
|
+
name = parts[2]
|
|
34
|
+
}
|
|
35
|
+
_, image, found := strings.Cut(value, "=")
|
|
36
|
+
if !found {
|
|
37
|
+
image = value
|
|
38
|
+
}
|
|
39
|
+
return name, image, nil
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func IsArgoApplication(object *unstructured.Unstructured) bool {
|
|
43
|
+
return object != nil && object.GetAPIVersion() == "argoproj.io/v1alpha1" && object.GetKind() == "Application"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ValidateArgoApplication defines the supported, single-namespace GitOps path.
|
|
47
|
+
// AppProject and repository credentials remain administrator-managed in Argo CD.
|
|
48
|
+
func ValidateArgoApplication(object *unstructured.Unstructured) error {
|
|
49
|
+
if !IsArgoApplication(object) || object.GetNamespace() == "" || object.GetName() == "" {
|
|
50
|
+
return fmt.Errorf("GitOps requires a namespaced Argo CD Application")
|
|
51
|
+
}
|
|
52
|
+
if object.GetDeletionTimestamp() != nil || len(object.GetOwnerReferences()) != 0 || len(object.GetFinalizers()) != 0 {
|
|
53
|
+
return fmt.Errorf("GitOps Application must be an independent, non-deleting root without cascading deletion")
|
|
54
|
+
}
|
|
55
|
+
for key := range object.GetAnnotations() {
|
|
56
|
+
if strings.HasPrefix(key, "argocd.argoproj.io/") {
|
|
57
|
+
return fmt.Errorf("GitOps Application must not override controller behavior with annotations")
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
spec, _, err := unstructured.NestedMap(object.Object, "spec")
|
|
61
|
+
if err != nil {
|
|
62
|
+
return err
|
|
63
|
+
}
|
|
64
|
+
if err := argoAllowedFields(spec, "project", "source", "destination", "syncPolicy"); err != nil {
|
|
65
|
+
return err
|
|
66
|
+
}
|
|
67
|
+
if argoString(object, "spec", "project") == "" || argoString(object, "spec", "project") == "default" {
|
|
68
|
+
return fmt.Errorf("GitOps requires a dedicated restricted AppProject")
|
|
69
|
+
}
|
|
70
|
+
destination, _, err := unstructured.NestedStringMap(object.Object, "spec", "destination")
|
|
71
|
+
if err != nil || len(destination) != 2 || destination["namespace"] != object.GetNamespace() || destination["server"] != "https://kubernetes.default.svc" {
|
|
72
|
+
return fmt.Errorf("GitOps destination must be this cluster and the Application namespace")
|
|
73
|
+
}
|
|
74
|
+
source, _, err := unstructured.NestedMap(object.Object, "spec", "source")
|
|
75
|
+
if err != nil {
|
|
76
|
+
return err
|
|
77
|
+
}
|
|
78
|
+
if err := argoAllowedFields(source, "repoURL", "targetRevision", "path", "kustomize"); err != nil {
|
|
79
|
+
return err
|
|
80
|
+
}
|
|
81
|
+
revision := argoString(object, "spec", "source", "targetRevision")
|
|
82
|
+
if decoded, err := hex.DecodeString(revision); err != nil || len(decoded) != 20 && len(decoded) != 32 {
|
|
83
|
+
return fmt.Errorf("GitOps targetRevision must be a full immutable Git commit")
|
|
84
|
+
}
|
|
85
|
+
repository, err := url.Parse(argoString(object, "spec", "source", "repoURL"))
|
|
86
|
+
if err != nil || repository.Host == "" || repository.User != nil || repository.RawQuery != "" || repository.Fragment != "" || (repository.Scheme != "https" && repository.Scheme != "http" && repository.Scheme != "ssh") {
|
|
87
|
+
return fmt.Errorf("GitOps repository must be an HTTP(S) or SSH URL without embedded credentials")
|
|
88
|
+
}
|
|
89
|
+
directory := argoString(object, "spec", "source", "path")
|
|
90
|
+
if directory == "" || path.IsAbs(directory) || path.Clean(directory) != directory || directory == ".." || strings.HasPrefix(directory, "../") || strings.Contains(directory, "\\") {
|
|
91
|
+
return fmt.Errorf("GitOps path must stay within the pinned repository")
|
|
92
|
+
}
|
|
93
|
+
kustomize, _, err := unstructured.NestedMap(object.Object, "spec", "source", "kustomize")
|
|
94
|
+
if err != nil {
|
|
95
|
+
return err
|
|
96
|
+
}
|
|
97
|
+
if err := argoAllowedFields(kustomize, "images", "namespace"); err != nil {
|
|
98
|
+
return err
|
|
99
|
+
}
|
|
100
|
+
if argoString(object, "spec", "source", "kustomize", "namespace") != object.GetNamespace() {
|
|
101
|
+
return fmt.Errorf("GitOps Kustomize namespace must be explicitly fixed to the delivery namespace")
|
|
102
|
+
}
|
|
103
|
+
images, _, err := unstructured.NestedStringSlice(object.Object, "spec", "source", "kustomize", "images")
|
|
104
|
+
if err != nil || len(images) == 0 || len(images) > 16 {
|
|
105
|
+
return fmt.Errorf("GitOps requires 1 to 16 fixed Kustomize image overrides")
|
|
106
|
+
}
|
|
107
|
+
imageNames := map[string]bool{}
|
|
108
|
+
for _, image := range images {
|
|
109
|
+
name, _, err := ParseArgoImageOverride(image)
|
|
110
|
+
if err != nil {
|
|
111
|
+
return err
|
|
112
|
+
}
|
|
113
|
+
if imageNames[name] {
|
|
114
|
+
return fmt.Errorf("GitOps image overrides must use unique image names")
|
|
115
|
+
}
|
|
116
|
+
imageNames[name] = true
|
|
117
|
+
}
|
|
118
|
+
policy, _, err := unstructured.NestedMap(object.Object, "spec", "syncPolicy")
|
|
119
|
+
if err != nil {
|
|
120
|
+
return err
|
|
121
|
+
}
|
|
122
|
+
if err := argoAllowedFields(policy, "syncOptions"); err != nil {
|
|
123
|
+
return err
|
|
124
|
+
}
|
|
125
|
+
options, _, err := unstructured.NestedStringSlice(object.Object, "spec", "syncPolicy", "syncOptions")
|
|
126
|
+
if err != nil || !reflect.DeepEqual(options, []string{"FailOnSharedResource=true"}) {
|
|
127
|
+
return fmt.Errorf("GitOps requires manual sync and FailOnSharedResource=true")
|
|
128
|
+
}
|
|
129
|
+
return nil
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
func argoAllowedFields(object map[string]any, allowed ...string) error {
|
|
133
|
+
for key := range object {
|
|
134
|
+
if !slices.Contains(allowed, key) {
|
|
135
|
+
return fmt.Errorf("unsupported GitOps field %q", key)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return nil
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
func argoString(object *unstructured.Unstructured, fields ...string) string {
|
|
142
|
+
value, _, _ := unstructured.NestedString(object.Object, fields...)
|
|
143
|
+
return value
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
func argoOperationID(object *unstructured.Unstructured, fields ...string) string {
|
|
147
|
+
items, _, _ := unstructured.NestedSlice(object.Object, append(fields, "info")...)
|
|
148
|
+
for _, raw := range items {
|
|
149
|
+
item, _ := raw.(map[string]any)
|
|
150
|
+
if item["name"] == ArgoOperationAnnotation {
|
|
151
|
+
value, _ := item["value"].(string)
|
|
152
|
+
return value
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return ""
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
var argoResourceTypes = map[string]schema.GroupVersionResource{
|
|
159
|
+
"apps/Deployment": {Group: "apps", Version: "v1", Resource: "deployments"},
|
|
160
|
+
"apps/StatefulSet": {Group: "apps", Version: "v1", Resource: "statefulsets"},
|
|
161
|
+
"/Service": {Version: "v1", Resource: "services"},
|
|
162
|
+
"/ConfigMap": {Version: "v1", Resource: "configmaps"},
|
|
163
|
+
"/Secret": {Version: "v1", Resource: "secrets"},
|
|
164
|
+
"/ServiceAccount": {Version: "v1", Resource: "serviceaccounts"},
|
|
165
|
+
"/PersistentVolumeClaim": {Version: "v1", Resource: "persistentvolumeclaims"},
|
|
166
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
package runtime
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"errors"
|
|
7
|
+
"net/url"
|
|
8
|
+
"os"
|
|
9
|
+
"strings"
|
|
10
|
+
"testing"
|
|
11
|
+
"time"
|
|
12
|
+
|
|
13
|
+
"github.com/google/uuid"
|
|
14
|
+
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
15
|
+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
16
|
+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
17
|
+
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
18
|
+
"k8s.io/apimachinery/pkg/util/wait"
|
|
19
|
+
"k8s.io/client-go/dynamic"
|
|
20
|
+
"k8s.io/client-go/kubernetes"
|
|
21
|
+
"k8s.io/client-go/tools/clientcmd"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
func TestArgoCDWithKubernetes(t *testing.T) {
|
|
25
|
+
kubeconfig := os.Getenv("SOHA_ARGOCD_TEST_KUBECONFIG")
|
|
26
|
+
if kubeconfig == "" {
|
|
27
|
+
t.Skip("set explicit loopback kubeconfig and SOHA_ARGOCD_TEST_STATE for the private GitOps fixture")
|
|
28
|
+
}
|
|
29
|
+
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
|
30
|
+
operatorRequire(t, err)
|
|
31
|
+
endpoint, err := url.Parse(config.Host)
|
|
32
|
+
if err != nil || endpoint.Hostname() != "127.0.0.1" {
|
|
33
|
+
t.Fatal("only the loopback fixture cluster is supported")
|
|
34
|
+
}
|
|
35
|
+
config.Timeout = 15 * time.Second
|
|
36
|
+
client, err := dynamic.NewForConfig(config)
|
|
37
|
+
operatorRequire(t, err)
|
|
38
|
+
kube, err := kubernetes.NewForConfig(config)
|
|
39
|
+
operatorRequire(t, err)
|
|
40
|
+
data, err := os.ReadFile(os.Getenv("SOHA_ARGOCD_TEST_STATE"))
|
|
41
|
+
operatorRequire(t, err)
|
|
42
|
+
var state map[string]json.RawMessage
|
|
43
|
+
operatorRequire(t, json.Unmarshal(data, &state))
|
|
44
|
+
value := func(key string) string {
|
|
45
|
+
var value string
|
|
46
|
+
operatorRequire(t, json.Unmarshal(state[key], &value))
|
|
47
|
+
return value
|
|
48
|
+
}
|
|
49
|
+
namespace, name := "soha-workflow-r6-gitops", "r6-gitops-runtime"
|
|
50
|
+
app := &unstructured.Unstructured{Object: map[string]any{"apiVersion": "argoproj.io/v1alpha1", "kind": "Application", "metadata": map[string]any{"name": name, "namespace": namespace}, "spec": map[string]any{"project": "r6-gitops", "source": map[string]any{"repoURL": value("repositoryURL"), "path": ".", "targetRevision": value("v1"), "kustomize": map[string]any{"namespace": namespace, "images": []any{"app=" + value("image")}}}, "destination": map[string]any{"server": "https://kubernetes.default.svc", "namespace": namespace}, "syncPolicy": map[string]any{"syncOptions": []any{"FailOnSharedResource=true"}}}}}
|
|
51
|
+
children := func(version string) []*unstructured.Unstructured {
|
|
52
|
+
var documents map[string][]*unstructured.Unstructured
|
|
53
|
+
operatorRequire(t, json.Unmarshal(state["documents"], &documents))
|
|
54
|
+
if len(documents[version]) != 2 {
|
|
55
|
+
t.Fatal("frozen fixture requires two source-derived child documents")
|
|
56
|
+
}
|
|
57
|
+
return documents[version]
|
|
58
|
+
}
|
|
59
|
+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
60
|
+
defer cancel()
|
|
61
|
+
resources := client.Resource(argoApplications).Namespace(namespace)
|
|
62
|
+
options := metav1.PatchOptions{FieldManager: "soha-r6-gitops-integration"}
|
|
63
|
+
id := uuid.NewString()
|
|
64
|
+
preflight := options
|
|
65
|
+
preflight.DryRun = []string{metav1.DryRunAll}
|
|
66
|
+
_, err = ApplyArgoApplication(ctx, client, app, children("v1"), preflight, id+":preflight")
|
|
67
|
+
operatorRequire(t, err)
|
|
68
|
+
if _, err = resources.Get(ctx, name, metav1.GetOptions{}); !apierrors.IsNotFound(err) {
|
|
69
|
+
t.Fatal("preflight created an Application or fixture already exists")
|
|
70
|
+
}
|
|
71
|
+
t.Cleanup(func() {
|
|
72
|
+
if t.Failed() {
|
|
73
|
+
t.Log("failed fixture retained for diagnosis in its isolated namespace")
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
cleanup, stop := context.WithTimeout(context.Background(), 15*time.Second)
|
|
77
|
+
defer stop()
|
|
78
|
+
_ = resources.Delete(cleanup, name, metav1.DeleteOptions{})
|
|
79
|
+
for _, gvr := range []schema.GroupVersionResource{{Group: "apps", Version: "v1", Resource: "deployments"}, {Version: "v1", Resource: "services"}} {
|
|
80
|
+
_ = client.Resource(gvr).Namespace(namespace).Delete(cleanup, "r6-gitops-http", metav1.DeleteOptions{})
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
apply := func(version, operation string) {
|
|
84
|
+
operatorRequire(t, unstructured.SetNestedField(app.Object, value(version), "spec", "source", "targetRevision"))
|
|
85
|
+
live, err := ApplyArgoApplication(ctx, client, app, children(version), options, id+operation)
|
|
86
|
+
if err != nil {
|
|
87
|
+
var status any
|
|
88
|
+
if live != nil {
|
|
89
|
+
status = live.Object["status"]
|
|
90
|
+
}
|
|
91
|
+
t.Fatalf("sync %s: %v; status=%v", version, err, status)
|
|
92
|
+
}
|
|
93
|
+
err = wait.PollUntilContextTimeout(ctx, time.Second, 45*time.Second, true, func(ctx context.Context) (bool, error) {
|
|
94
|
+
live, err := resources.Get(ctx, name, metav1.GetOptions{})
|
|
95
|
+
if err != nil {
|
|
96
|
+
return false, err
|
|
97
|
+
}
|
|
98
|
+
observed, err := ObserveArgoApplication(ctx, client, app, live, children(version), options.FieldManager)
|
|
99
|
+
if err != nil {
|
|
100
|
+
return false, err
|
|
101
|
+
}
|
|
102
|
+
return observed.Health == "healthy" && observed.OperationID == id+operation && observed.Revision == value(version), nil
|
|
103
|
+
})
|
|
104
|
+
operatorRequire(t, err)
|
|
105
|
+
body, err := kube.CoreV1().RESTClient().Get().Namespace(namespace).Resource("services").Name("r6-gitops-http:8080").SubResource("proxy").DoRaw(ctx)
|
|
106
|
+
if err != nil || strings.TrimSpace(string(body)) != "gitops-"+version {
|
|
107
|
+
t.Fatalf("actual HTTP %s: %q %v", version, body, err)
|
|
108
|
+
}
|
|
109
|
+
t.Logf("Argo operation %s applied commit %s with owned, healthy resources and actual HTTP %s", operation, value(version), version)
|
|
110
|
+
}
|
|
111
|
+
apply("v1", ":deploy")
|
|
112
|
+
apply("v2", ":upgrade")
|
|
113
|
+
apply("v1", ":rollback")
|
|
114
|
+
operatorRequire(t, unstructured.SetNestedField(app.Object, value("hold"), "spec", "source", "targetRevision"))
|
|
115
|
+
stopping, stop := context.WithCancel(ctx)
|
|
116
|
+
observedHold := make(chan error, 1)
|
|
117
|
+
go func() {
|
|
118
|
+
err := wait.PollUntilContextTimeout(ctx, time.Second, 45*time.Second, true, func(ctx context.Context) (bool, error) {
|
|
119
|
+
live, err := resources.Get(ctx, name, metav1.GetOptions{})
|
|
120
|
+
if err != nil {
|
|
121
|
+
return false, err
|
|
122
|
+
}
|
|
123
|
+
return argoOperationID(live, "status", "operationState", "operation") == id+":cancel" && argoString(live, "status", "operationState", "phase") == "Running", nil
|
|
124
|
+
})
|
|
125
|
+
observedHold <- err
|
|
126
|
+
stop()
|
|
127
|
+
}()
|
|
128
|
+
live, err := ApplyArgoApplication(stopping, client, app, children("hold"), options, id+":cancel")
|
|
129
|
+
operatorRequire(t, <-observedHold)
|
|
130
|
+
if !errors.Is(err, context.Canceled) || live == nil || argoOperationActive(live) {
|
|
131
|
+
t.Fatalf("cancel was not confirmed: %v", err)
|
|
132
|
+
}
|
|
133
|
+
t.Log("controller confirmed cancellation; restoring through a new operation")
|
|
134
|
+
apply("v1", ":restore")
|
|
135
|
+
// The direct writer cannot adopt a resource managed by Argo CD, even with force.
|
|
136
|
+
deployment, err := client.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}).Namespace(namespace).Get(ctx, "r6-gitops-http", metav1.GetOptions{})
|
|
137
|
+
operatorRequire(t, err)
|
|
138
|
+
if ValidateDirectManifestOwner(deployment) == nil {
|
|
139
|
+
t.Fatal("direct writer accepted Argo CD-owned resource")
|
|
140
|
+
}
|
|
141
|
+
t.Logf("real Argo CD GitOps lifecycle passed for %s/%s", namespace, name)
|
|
142
|
+
}
|