@opensoha/contracts 0.1.26 → 0.1.29

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.
Files changed (213) hide show
  1. package/COMPATIBILITY.md +14 -3
  2. package/README.md +8 -9
  3. package/auth/catalog_generated.go +2 -2
  4. package/auth/permission-catalog.json +481 -1
  5. package/compat/consumer-revisions.json +1 -1
  6. package/compat/json-schema-ecosystem-0.1.0.json +1 -1
  7. package/compat/openapi-loose-boundaries-0.1.0.json +1 -1
  8. package/endpointcontrol/decode.go +64 -0
  9. package/endpointcontrol/models.go +138 -0
  10. package/endpointcontrol/models_test.go +51 -0
  11. package/endpointcontrol/protocol.schema.json +397 -0
  12. package/fixtures/json-schema/endpoint-controls/invalid/controls.json +16 -0
  13. package/fixtures/json-schema/endpoint-controls/invalid/missing-channel-decision.json +15 -0
  14. package/fixtures/json-schema/endpoint-controls/invalid/receipt-content.json +36 -0
  15. package/fixtures/json-schema/endpoint-controls/valid/controls.json +16 -0
  16. package/fixtures/json-schema/endpoint-controls/valid/receipt.json +35 -0
  17. package/fixtures/json-schema/network-runtime-protocol/invalid/eap-activate-bad-fingerprint.json +14 -0
  18. package/fixtures/json-schema/network-runtime-protocol/invalid/eap-gateway-request.json +14 -0
  19. package/fixtures/json-schema/network-runtime-protocol/invalid/eap-private-key-request.json +15 -0
  20. package/fixtures/json-schema/network-runtime-protocol/invalid/eap-profile-without-eap-credential.json +22 -0
  21. package/fixtures/json-schema/network-runtime-protocol/invalid/endpoint-dns-observation.json +25 -0
  22. package/fixtures/json-schema/network-runtime-protocol/invalid/gateway-presence.json +22 -0
  23. package/fixtures/json-schema/network-runtime-protocol/invalid/mihomo-protection-zero-version.json +45 -0
  24. package/fixtures/json-schema/network-runtime-protocol/invalid/presence-raw-key.json +23 -0
  25. package/fixtures/json-schema/network-runtime-protocol/invalid/presence-zero-sequence.json +22 -0
  26. package/fixtures/json-schema/network-runtime-protocol/invalid/workload-wide-vpn.json +17 -0
  27. package/fixtures/json-schema/network-runtime-protocol/valid/eap-certificate-activate.json +14 -0
  28. package/fixtures/json-schema/network-runtime-protocol/valid/eap-certificate-request.json +14 -0
  29. package/fixtures/json-schema/network-runtime-protocol/valid/eap-profile-enrollment.json +24 -0
  30. package/fixtures/json-schema/network-runtime-protocol/valid/endpoint-presence.json +22 -0
  31. package/fixtures/json-schema/network-runtime-protocol/valid/ipv6-configuration.json +102 -0
  32. package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-protected-policy.json +45 -0
  33. package/fixtures/json-schema/network-runtime-protocol/valid/site-dns-ipv6.json +25 -0
  34. package/fixtures/json-schema/network-runtime-protocol/valid/workload-direct-ztna.json +20 -0
  35. package/fixtures/openapi/DLPShadowDecision/invalid/step-up-type.json +14 -0
  36. package/fixtures/openapi/DLPShadowDecision/valid/archive-v2.json +14 -0
  37. package/fixtures/openapi/DLPShadowDecision/valid/step-up.json +14 -0
  38. package/fixtures/openapi/DLPShadowInput/invalid/self-reported-result.json +7 -0
  39. package/fixtures/openapi/DLPShadowInput/invalid/too-many-samples.json +14 -0
  40. package/fixtures/openapi/DLPShadowInput/valid/bounded.json +7 -0
  41. package/fixtures/openapi/DockerVolumeDLPAppealInput/invalid/p4-response.json +3 -0
  42. package/fixtures/openapi/DockerVolumeDLPAppealInput/valid/p4-response.json +3 -0
  43. package/fixtures/openapi/DockerVolumeDLPAppealReviewInput/invalid/p4-response.json +5 -0
  44. package/fixtures/openapi/DockerVolumeDLPAppealReviewInput/valid/p4-response.json +5 -0
  45. package/fixtures/openapi/DockerVolumeDLPAuthorizationInput/invalid/p4-response.json +3 -0
  46. package/fixtures/openapi/DockerVolumeDLPAuthorizationInput/valid/p4-response.json +3 -0
  47. package/fixtures/openapi/DockerVolumeDLPContent/invalid/archive-unsupported-channel.json +9 -0
  48. package/fixtures/openapi/DockerVolumeDLPContent/invalid/p4.json +6 -0
  49. package/fixtures/openapi/DockerVolumeDLPContent/invalid/step-up-audit.json +9 -0
  50. package/fixtures/openapi/DockerVolumeDLPContent/invalid/unavailable-engine.json +9 -0
  51. package/fixtures/openapi/DockerVolumeDLPContent/invalid/unknown-data-level.json +12 -0
  52. package/fixtures/openapi/DockerVolumeDLPContent/valid/classified-rule.json +16 -0
  53. package/fixtures/openapi/DockerVolumeDLPContent/valid/p4.json +8 -0
  54. package/fixtures/openapi/DockerVolumeDLPContent/valid/step-up.json +9 -0
  55. package/fixtures/openapi/DockerVolumeDLPDecision/invalid/p4.json +9 -0
  56. package/fixtures/openapi/DockerVolumeDLPDecision/invalid/step-up-type.json +12 -0
  57. package/fixtures/openapi/DockerVolumeDLPDecision/valid/archive-v2.json +12 -0
  58. package/fixtures/openapi/DockerVolumeDLPDecision/valid/p4.json +11 -0
  59. package/fixtures/openapi/DockerVolumeDLPDecision/valid/step-up.json +12 -0
  60. package/fixtures/openapi/DockerVolumeDLPSaveInput/invalid/p4.json +11 -0
  61. package/fixtures/openapi/DockerVolumeDLPSaveInput/valid/p4.json +11 -0
  62. package/fixtures/openapi/DockerVolumeFileReadInput/invalid/p4-response.json +5 -0
  63. package/fixtures/openapi/DockerVolumeFileReadInput/valid/p4-response.json +4 -0
  64. package/fixtures/openapi/EndpointDeviceInput/invalid/reported-management.json +5 -0
  65. package/fixtures/openapi/EndpointDeviceInput/valid/management-state.json +5 -0
  66. package/fixtures/openapi/EndpointDeviceRegistrationInput/invalid/self-promote-management.json +5 -0
  67. package/fixtures/openapi/KubernetesClusterRegistrationRequest/invalid/metric-cluster-value-length.json +3 -0
  68. package/fixtures/openapi/KubernetesClusterRegistrationRequest/invalid/metric-cluster-value-type.json +3 -0
  69. package/fixtures/openapi/KubernetesClusterRegistrationRequest/valid/metric-cluster-alias.json +5 -0
  70. package/fixtures/openapi/KubernetesClusterRegistrationRequest/valid/metric-cluster-reset.json +3 -0
  71. package/fixtures/openapi/KubernetesHelmReleaseValuesUpdateInput/invalid/revision-overflow.json +4 -0
  72. package/fixtures/openapi/KubernetesHelmReleaseValuesUpdateInput/valid/legacy.json +3 -0
  73. package/fixtures/openapi/KubernetesHelmReleaseValuesUpdateInput/valid/observed-revision.json +4 -0
  74. package/fixtures/openapi/KubernetesJobDetail/invalid/execution-observation.json +23 -0
  75. package/fixtures/openapi/KubernetesJobDetail/valid/execution-observation.json +23 -0
  76. package/fixtures/openapi/KubernetesNode/invalid/labels.json +10 -0
  77. package/fixtures/openapi/KubernetesNode/valid/labels.json +11 -0
  78. package/fixtures/openapi/KubernetesPodListEnvelope/invalid/empty-uid.json +13 -0
  79. package/fixtures/openapi/KubernetesPodListEnvelope/invalid/invalid-container-state.json +20 -0
  80. package/fixtures/openapi/KubernetesPodListEnvelope/invalid/missing-container-ready.json +19 -0
  81. package/fixtures/openapi/KubernetesPodListEnvelope/valid/container-statuses.json +67 -0
  82. package/fixtures/openapi/KubernetesPodListEnvelope/valid/running-pod.json +1 -0
  83. package/fixtures/openapi/KubernetesPrometheusDetail/invalid/metric-cluster-value-type.json +4 -0
  84. package/fixtures/openapi/KubernetesPrometheusDetail/valid/metric-cluster-alias.json +5 -0
  85. package/fixtures/openapi/KubernetesResourceGraphEnvelope/invalid/network-edge-source-payload.json +73 -0
  86. package/fixtures/openapi/KubernetesResourceGraphEnvelope/valid/network-edge-facts.json +67 -0
  87. package/fixtures/openapi/KubernetesSecretCertificate/invalid/secret-data.json +1 -0
  88. package/fixtures/openapi/KubernetesSecretCertificate/invalid/status.json +1 -0
  89. package/fixtures/openapi/KubernetesSecretCertificate/valid/available.json +1 -0
  90. package/fixtures/openapi/KubernetesSecretCertificate/valid/missing.json +1 -0
  91. package/fixtures/openapi/ManagedContentPasteInput/invalid/boundary.json +5 -0
  92. package/fixtures/openapi/ManagedContentPasteInput/valid/boundary.json +6 -0
  93. package/fixtures/openapi/ManagedContentPolicy/invalid/self-reported-allow.json +8 -0
  94. package/fixtures/openapi/ManagedContentPolicy/invalid/step-up-audit.json +8 -0
  95. package/fixtures/openapi/ManagedContentPolicy/invalid/unknown-extractor.json +8 -0
  96. package/fixtures/openapi/ManagedContentPolicy/valid/archive-v2.json +8 -0
  97. package/fixtures/openapi/ManagedContentPolicy/valid/network-and-level.json +10 -0
  98. package/fixtures/openapi/ManagedContentPolicy/valid/step-up.json +8 -0
  99. package/fixtures/openapi/ManagedContentReviewInput/invalid/boundary.json +5 -0
  100. package/fixtures/openapi/ManagedContentReviewInput/valid/boundary.json +5 -0
  101. package/fixtures/openapi/ManagedContentSaveInput/invalid/boundary.json +14 -0
  102. package/fixtures/openapi/ManagedContentSaveInput/valid/boundary.json +16 -0
  103. package/fixtures/openapi/ManagedContentUploadInput/invalid/boundary.json +7 -0
  104. package/fixtures/openapi/ManagedContentUploadInput/valid/boundary.json +6 -0
  105. package/fixtures/openapi/NetworkAccessPolicyInput/invalid/unknown-management-state.json +35 -0
  106. package/fixtures/openapi/NetworkAccessPolicyInput/valid/managed-device.json +35 -0
  107. package/fixtures/openapi/NetworkAdmissionPreviewRequest/invalid/deviceId-characters.json +4 -0
  108. package/fixtures/openapi/NetworkAdmissionPreviewRequest/invalid/nasBindingId-characters.json +4 -0
  109. package/fixtures/openapi/NetworkAdmissionPreviewRequest/invalid/subject-override.json +1 -0
  110. package/fixtures/openapi/NetworkAdmissionPreviewRequest/valid/device-nas.json +1 -0
  111. package/fixtures/openapi/NetworkAdmissionPreviewResult/invalid/missing-attributeVersion.json +13 -0
  112. package/fixtures/openapi/NetworkAdmissionPreviewResult/invalid/missing-evaluatedAt.json +13 -0
  113. package/fixtures/openapi/NetworkAdmissionPreviewResult/valid/versioned.json +14 -0
  114. package/fixtures/openapi/NetworkConflictAnalysisRequest/invalid/missing-runtime-ranges.json +1 -0
  115. package/fixtures/openapi/NetworkConflictAnalysisRequest/invalid/unknown-source-type.json +9 -0
  116. package/fixtures/openapi/NetworkConflictAnalysisRequest/valid/empty-runtime-ranges.json +3 -0
  117. package/fixtures/openapi/NetworkConflictAnalysisRequest/{invalid → valid}/ipv6-range.json +1 -1
  118. package/fixtures/openapi/NetworkEAPServerTrust/invalid/no-server-validation.json +5 -0
  119. package/fixtures/openapi/NetworkEAPServerTrust/valid/named-server.json +9 -0
  120. package/fixtures/openapi/NetworkEndpointResponsePolicyInput/invalid/all-sites-implicit.json +45 -0
  121. package/fixtures/openapi/NetworkEndpointResponsePolicyInput/invalid/unbounded-stages.json +54 -0
  122. package/fixtures/openapi/NetworkEndpointResponsePolicyInput/invalid/zero-heartbeat.json +47 -0
  123. package/fixtures/openapi/NetworkEndpointResponsePolicyInput/valid/staged-protection.json +47 -0
  124. package/fixtures/openapi/NetworkEndpointResponseRestoreInput/invalid/missing-reason.json +3 -0
  125. package/fixtures/openapi/NetworkEndpointResponseRestoreInput/valid/expected-current.json +4 -0
  126. package/fixtures/openapi/NetworkPolicyPreviewResult/invalid/attribute-version.json +1 -0
  127. package/fixtures/openapi/NetworkPolicyPreviewResult/valid/admission-version.json +1 -0
  128. package/fixtures/openapi/NetworkPolicySubjects/invalid/duplicate-role.json +1 -0
  129. package/fixtures/openapi/NetworkPolicySubjects/invalid/role-characters.json +5 -0
  130. package/fixtures/openapi/NetworkPolicySubjects/valid/role-bound.json +1 -0
  131. package/fixtures/openapi/NetworkProxyConfigurationInput/invalid/missing-content.json +1 -0
  132. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/invalid/bad-binding-id.json +9 -0
  133. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/invalid/empty-content.json +1 -0
  134. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/valid/binding-only.json +11 -0
  135. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/valid/clear-binding-disabled.json +5 -0
  136. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/valid/enabled.json +1 -0
  137. package/fixtures/openapi/NetworkSiteInput/invalid/unknown-governance.json +6 -0
  138. package/fixtures/openapi/NetworkSiteInput/valid/restricted-unmanaged.json +6 -0
  139. package/gen/go/sohaapi/client.go +1 -1
  140. package/gen/go/sohaapi/client_test.go +13 -0
  141. package/gen/go/sohaapi/kubernetes_workload_optional_test.go +40 -0
  142. package/gen/go/sohaapi/pod_status_compat_test.go +36 -0
  143. package/gen/go/sohaapi/proxy_configuration_test.go +38 -0
  144. package/gen/go/sohaapi/types.go +3222 -260
  145. package/gen/ts/sohaapi/index.ts +15506 -10104
  146. package/helmrelease/runtime/values_upgrade.go +78 -0
  147. package/helmrelease/runtime/values_upgrade_test.go +123 -0
  148. package/helmrelease/values_update.go +31 -0
  149. package/network/README.md +34 -0
  150. package/network/network-radius-accounting.schema.json +4 -1
  151. package/network/network-runtime-protocol.schema.json +2166 -317
  152. package/network/proxy-runtime.schema.json +482 -67
  153. package/openapi/network_control_test.go +64 -0
  154. package/openapi/soha-api.json +50412 -41310
  155. package/openapi/soha-api.yaml +16859 -10699
  156. package/openapi/spec_generated.go +3 -3
  157. package/package.json +6 -3
  158. package/proxytransport/handler.go +271 -0
  159. package/proxytransport/probe.go +30 -0
  160. package/proxytransport/scope.go +89 -0
  161. package/proxytransport/transport_test.go +60 -0
  162. package/resource/agent_profiles.go +9 -15
  163. package/resource/agent_profiles_test.go +9 -3
  164. package/resource/custom_graph_page.go +18 -0
  165. package/resource/models.go +102 -57
  166. package/resource/network_topology.go +22 -0
  167. package/resource/network_topology_projection.go +36 -22
  168. package/resource/network_topology_projection_test.go +4 -0
  169. package/resource/network_topology_test.go +20 -0
  170. package/resource/network_topology_validation.go +5 -0
  171. package/resource/observed_workload.go +18 -0
  172. package/resource/pod_status_test.go +22 -0
  173. package/resource/rbac_graph.go +4 -0
  174. package/resource/rollout_optional_test.go +33 -0
  175. package/resource/runtime/network_relationship_primitives.go +66 -0
  176. package/resource/runtime/network_relationship_primitives_test.go +147 -0
  177. package/resource/runtime/network_topology.go +17 -5
  178. package/resource/runtime/network_topology_builder.go +405 -0
  179. package/resource/runtime/network_topology_builder_backends.go +195 -0
  180. package/resource/runtime/network_topology_builder_gateway_l4_test.go +86 -0
  181. package/resource/runtime/network_topology_builder_ingress.go +302 -0
  182. package/resource/runtime/network_topology_builder_legacy.go +66 -0
  183. package/resource/runtime/network_topology_builder_providers.go +242 -0
  184. package/resource/runtime/network_topology_builder_test.go +186 -0
  185. package/resource/runtime/network_topology_declarations.go +329 -0
  186. package/resource/runtime/resource_edit.go +22 -0
  187. package/resource/runtime/resource_edit_test.go +32 -0
  188. package/resource/runtime/resource_graph_collect.go +325 -0
  189. package/resource/runtime/resource_graph_configuration.go +59 -0
  190. package/resource/runtime/resource_graph_configuration_collect.go +198 -0
  191. package/resource/runtime/resource_graph_configuration_test.go +470 -0
  192. package/resource/runtime/resource_graph_custom_page.go +159 -0
  193. package/resource/runtime/resource_graph_custom_root.go +132 -0
  194. package/resource/runtime/resource_graph_custom_root_test.go +197 -0
  195. package/resource/runtime/resource_graph_enrichment.go +417 -0
  196. package/resource/runtime/resource_graph_enrichment_hpa_test.go +117 -0
  197. package/resource/runtime/resource_graph_enrichment_test.go +201 -0
  198. package/resource/runtime/resource_graph_metadata.go +125 -0
  199. package/resource/runtime/resource_graph_metadata_test.go +204 -0
  200. package/resource/runtime/resource_graph_network.go +224 -0
  201. package/resource/runtime/resource_graph_network_collect.go +174 -0
  202. package/resource/runtime/resource_graph_network_collect_test.go +147 -0
  203. package/resource/runtime/resource_graph_network_test.go +342 -0
  204. package/resource/runtime/resource_graph_rbac.go +278 -0
  205. package/resource/runtime/resource_graph_rbac_test.go +372 -0
  206. package/resource/runtime/resource_graph_rules.go +834 -0
  207. package/resource/runtime/resource_graph_storage.go +144 -0
  208. package/resource/runtime/resource_graph_storage_collect.go +163 -0
  209. package/resource/runtime/resource_graph_storage_test.go +397 -0
  210. package/resource/runtime/resource_graph_test.go +627 -0
  211. package/resource/runtime/secret_certificate.go +44 -0
  212. package/resource/runtime/secret_certificate_test.go +87 -0
  213. package/resource/storage_graph.go +4 -0
@@ -0,0 +1,144 @@
1
+ package runtime
2
+
3
+ import (
4
+ cr "github.com/opensoha/soha-contracts/resource"
5
+ corev1 "k8s.io/api/core/v1"
6
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
7
+ )
8
+
9
+ const (
10
+ ResourceGraphRelationMounts = "mounts"
11
+ ResourceGraphRelationBindsTo = "binds-to"
12
+ ResourceGraphRelationUsesStorageClass = "uses-storage-class"
13
+ )
14
+
15
+ func graphStorageRootKind(kind string) bool {
16
+ switch kind {
17
+ case "PersistentVolumeClaim", "PersistentVolume", "StorageClass":
18
+ return true
19
+ }
20
+ return false
21
+ }
22
+
23
+ func graphStorageClassName(o *unstructured.Unstructured) string {
24
+ name, _, _ := unstructured.NestedString(o.Object, "spec", "storageClassName")
25
+ return name
26
+ }
27
+
28
+ func graphClaimVolumeName(claim *unstructured.Unstructured) string {
29
+ name, _, _ := unstructured.NestedString(claim.Object, "spec", "volumeName")
30
+ return name
31
+ }
32
+
33
+ func graphVolumeClaimName(volume map[string]interface{}) string {
34
+ return graphString(graphMap(volume, "persistentVolumeClaim"), "claimName")
35
+ }
36
+
37
+ func graphVolumeClaimRef(volume *unstructured.Unstructured) (graphNamespacedName, string) {
38
+ ref, _, _ := graphNestedMap(volume.Object, "spec", "claimRef")
39
+ if api := graphString(ref, "apiVersion"); api != "" && api != "v1" {
40
+ return graphNamespacedName{}, ""
41
+ }
42
+ if kind := graphString(ref, "kind"); kind != "" && kind != "PersistentVolumeClaim" {
43
+ return graphNamespacedName{}, ""
44
+ }
45
+ return graphNamespacedName{namespace: graphString(ref, "namespace"), name: graphString(ref, "name")}, graphString(ref, "uid")
46
+ }
47
+
48
+ func graphStorageBound(claim, volume *unstructured.Unstructured) bool {
49
+ if claim == nil || volume == nil || claim.GetDeletionTimestamp() != nil || volume.GetDeletionTimestamp() != nil {
50
+ return false
51
+ }
52
+ if graphStatus(claim) != string(corev1.ClaimBound) || graphStatus(volume) != string(corev1.VolumeBound) {
53
+ return false
54
+ }
55
+ ref, uid := graphVolumeClaimRef(volume)
56
+ return ref.namespace != "" && ref.name != "" && uid != "" && uid == string(claim.GetUID()) &&
57
+ ref.namespace == claim.GetNamespace() && ref.name == claim.GetName() && graphClaimVolumeName(claim) == volume.GetName()
58
+ }
59
+
60
+ func (b *graphRules) storageRelations(o *unstructured.Unstructured, id string) {
61
+ if !graphBuiltinSpec(o) {
62
+ return
63
+ }
64
+ switch o.GetKind() {
65
+ case "Pod":
66
+ for _, raw := range graphSlice(graphPodSpec(o), "volumes") {
67
+ if !b.active() {
68
+ return
69
+ }
70
+ volume, _ := raw.(map[string]interface{})
71
+ b.storageMountEdge(id, o.GetNamespace(), graphVolumeClaimName(volume))
72
+ }
73
+ return
74
+ case "PersistentVolumeClaim":
75
+ if o.GetNamespace() == "" {
76
+ return
77
+ }
78
+ if volumeName := graphClaimVolumeName(o); volumeName != "" {
79
+ volume := b.objects[graphIdentity("v1", "PersistentVolume", "", volumeName)]
80
+ edge := cr.ResourceGraphEdge{SourceID: id, Relation: ResourceGraphRelationBindsTo, FieldPath: "spec.volumeName"}
81
+ if graphStorageBound(o, volume) {
82
+ edge.TargetID = b.node(b.ref(volume), graphStatus(volume))
83
+ edge.ResolutionState = "resolved"
84
+ edge.Note = "PVC and PV report Bound with matching references"
85
+ } else {
86
+ edge.TargetID = b.unresolvedStorageNode("PersistentVolume", "", volumeName, "")
87
+ edge.ResolutionState = "unresolved"
88
+ edge.Note = "PVC volume reference requires binding confirmation"
89
+ }
90
+ b.addEdge(edge)
91
+ }
92
+ case "PersistentVolume":
93
+ if o.GetNamespace() != "" {
94
+ return
95
+ }
96
+ ref, uid := graphVolumeClaimRef(o)
97
+ if ref.namespace != "" && ref.name != "" {
98
+ claim := b.objects[graphIdentity("v1", "PersistentVolumeClaim", ref.namespace, ref.name)]
99
+ if !graphStorageBound(claim, o) {
100
+ declaration := b.ref(o)
101
+ b.addEdge(cr.ResourceGraphEdge{
102
+ SourceID: b.unresolvedStorageNode("PersistentVolumeClaim", ref.namespace, ref.name, uid),
103
+ TargetID: id, Relation: ResourceGraphRelationBindsTo, ResolutionState: "unresolved",
104
+ FieldPath: "spec.claimRef", Note: "PV claim reference requires binding confirmation", SourceRef: &declaration,
105
+ })
106
+ }
107
+ }
108
+ default:
109
+ return
110
+ }
111
+ b.storageReferenceEdge(id, "storage.k8s.io/v1", "StorageClass", "", graphStorageClassName(o),
112
+ ResourceGraphRelationUsesStorageClass, "spec.storageClassName", "StorageClass reference declared in spec")
113
+ }
114
+
115
+ func (b *graphRules) storageMountEdge(source, namespace, claim string) {
116
+ if namespace != "" {
117
+ b.storageReferenceEdge(source, "v1", "PersistentVolumeClaim", namespace, claim,
118
+ ResourceGraphRelationMounts, "spec.volumes", "PVC reference declared in Pod spec")
119
+ }
120
+ }
121
+
122
+ func (b *graphRules) unresolvedStorageNode(kind, namespace, name, uid string) string {
123
+ ref := cr.ResourceRef{ClusterID: b.cluster, APIVersion: "v1", Kind: kind, Namespace: namespace, Name: name, UID: uid, ScopeMode: topologyScope(namespace != "")}
124
+ id := graphNodeID(ref)
125
+ if observed := b.objects[graphIdentity(ref.APIVersion, kind, namespace, name)]; observed != nil && graphNodeID(b.ref(observed)) == id {
126
+ // A one-sided declaration can name an observed object without binding it.
127
+ id += ":unresolved"
128
+ }
129
+ return b.addNode(id, ref, "unresolved")
130
+ }
131
+
132
+ func (b *graphRules) storageReferenceEdge(source, api, kind, namespace, name, relation, fieldPath, note string) {
133
+ if name == "" {
134
+ return
135
+ }
136
+ state := "unresolved"
137
+ if b.objects[graphIdentity(api, kind, namespace, name)] != nil {
138
+ state = "resolved"
139
+ }
140
+ b.addEdge(cr.ResourceGraphEdge{
141
+ SourceID: source, TargetID: b.target(api, kind, namespace, name, ""), Relation: relation,
142
+ ResolutionState: state, FieldPath: fieldPath, Note: note,
143
+ })
144
+ }
@@ -0,0 +1,163 @@
1
+ package runtime
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+
7
+ corev1 "k8s.io/api/core/v1"
8
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
10
+ kruntime "k8s.io/apimachinery/pkg/runtime"
11
+ "k8s.io/apimachinery/pkg/runtime/schema"
12
+ "k8s.io/client-go/kubernetes"
13
+ )
14
+
15
+ func graphStorageProjection(obj kruntime.Object, projected *unstructured.Unstructured) error {
16
+ kind := projected.GetKind()
17
+ _, metadataOnly := obj.(*metav1.PartialObjectMetadata)
18
+ if gvk := obj.GetObjectKind().GroupVersionKind(); !metadataOnly && !gvk.Empty() && gvk != schema.FromAPIVersionAndKind(projected.GetAPIVersion(), kind) {
19
+ return errors.New("storage relationship observation GVK mismatch")
20
+ }
21
+ if projected.GetName() == "" || (kind == "PersistentVolumeClaim") != (projected.GetNamespace() != "") {
22
+ return errors.New("storage relationship observation scope mismatch")
23
+ }
24
+ switch item := obj.(type) {
25
+ case *corev1.PersistentVolumeClaim:
26
+ spec := map[string]interface{}{"volumeName": item.Spec.VolumeName}
27
+ if item.Spec.StorageClassName != nil {
28
+ spec["storageClassName"] = *item.Spec.StorageClassName
29
+ }
30
+ projected.Object["spec"] = spec
31
+ projected.Object["status"] = map[string]interface{}{"phase": string(item.Status.Phase)}
32
+ if item.DeletionTimestamp != nil {
33
+ projected.SetDeletionTimestamp(item.DeletionTimestamp)
34
+ }
35
+ case *corev1.PersistentVolume:
36
+ spec := map[string]interface{}{"storageClassName": item.Spec.StorageClassName}
37
+ if ref := item.Spec.ClaimRef; ref != nil {
38
+ spec["claimRef"] = map[string]interface{}{"apiVersion": ref.APIVersion, "kind": ref.Kind, "namespace": ref.Namespace, "name": ref.Name, "uid": string(ref.UID)}
39
+ }
40
+ projected.Object["spec"] = spec
41
+ projected.Object["status"] = map[string]interface{}{"phase": string(item.Status.Phase)}
42
+ if item.DeletionTimestamp != nil {
43
+ projected.SetDeletionTimestamp(item.DeletionTimestamp)
44
+ }
45
+ }
46
+ return nil
47
+ }
48
+
49
+ func collectGraphStorage(ctx context.Context, client kubernetes.Interface, sources map[string]graphSourceDefinition, root *unstructured.Unstructured, objects *[]*unstructured.Unstructured, warnings *[]string) error {
50
+ var claim, volume *unstructured.Unstructured
51
+ var err error
52
+ switch root.GetKind() {
53
+ case "PersistentVolumeClaim":
54
+ claim = root
55
+ volume, err = collectGraphStorageTarget(ctx, "PersistentVolume", "", graphClaimVolumeName(claim), client.CoreV1().PersistentVolumes().Get, objects, warnings)
56
+ case "PersistentVolume":
57
+ volume = root
58
+ ref, _ := graphVolumeClaimRef(volume)
59
+ if ref.namespace != "" {
60
+ claim, err = collectGraphStorageTarget(ctx, "PersistentVolumeClaim", ref.namespace, ref.name, client.CoreV1().PersistentVolumeClaims(ref.namespace).Get, objects, warnings)
61
+ }
62
+ case "StorageClass":
63
+ return collectGraphStorageClassSources(ctx, client, sources, root.GetName(), objects, warnings)
64
+ default:
65
+ return nil
66
+ }
67
+ if err != nil {
68
+ return err
69
+ }
70
+ class := graphStorageClassName(root)
71
+ getClass := client.StorageV1().StorageClasses().Get
72
+ if _, err = collectGraphStorageTarget(ctx, "StorageClass", "", class, getClass, objects, warnings); err != nil {
73
+ return err
74
+ }
75
+ bound := graphStorageBound(claim, volume)
76
+ if bound && root == claim && graphStorageClassName(volume) != class {
77
+ if _, err = collectGraphStorageTarget(ctx, "StorageClass", "", graphStorageClassName(volume), getClass, objects, warnings); err != nil {
78
+ return err
79
+ }
80
+ }
81
+ if root != claim && !bound {
82
+ return ctx.Err()
83
+ }
84
+ ref := graphNamespacedName{namespace: claim.GetNamespace(), name: claim.GetName()}
85
+ return collectGraphStoragePods(ctx, client, ref.namespace, map[graphNamespacedName]bool{ref: true}, objects, warnings)
86
+ }
87
+
88
+ func collectGraphStorageTarget[T kruntime.Object](ctx context.Context, kind, namespace, name string, get func(context.Context, string, metav1.GetOptions) (T, error), objects *[]*unstructured.Unstructured, warnings *[]string) (*unstructured.Unstructured, error) {
89
+ if err := ctx.Err(); err != nil || name == "" {
90
+ return nil, err
91
+ }
92
+ item, err := get(ctx, name, metav1.GetOptions{})
93
+ if err != nil {
94
+ if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
95
+ return nil, err
96
+ }
97
+ *warnings = append(*warnings, kind+" reference source "+graphSourceErrorClass(err))
98
+ return nil, ctx.Err()
99
+ }
100
+ projected, err := graphObjectProjection(item, graphBuiltinAPIs[kind], kind)
101
+ if err != nil || projected.GetName() != name || projected.GetNamespace() != namespace {
102
+ *warnings = append(*warnings, kind+" reference source projection unavailable")
103
+ return nil, ctx.Err()
104
+ }
105
+ *objects = append(*objects, projected)
106
+ return projected, ctx.Err()
107
+ }
108
+
109
+ func collectGraphStorageClassSources(ctx context.Context, client kubernetes.Interface, sources map[string]graphSourceDefinition, name string, objects *[]*unstructured.Unstructured, warnings *[]string) error {
110
+ volumesStart := len(*objects)
111
+ if err := sources["PersistentVolume"].collect(ctx, objects, warnings); err != nil {
112
+ return err
113
+ }
114
+ classVolumes := map[string]*unstructured.Unstructured{}
115
+ for _, volume := range (*objects)[volumesStart:] {
116
+ if graphStorageClassName(volume) == name && classVolumes[volume.GetName()] == nil {
117
+ classVolumes[volume.GetName()] = volume
118
+ }
119
+ }
120
+ claimsStart := len(*objects)
121
+ if err := sources["PersistentVolumeClaim"].collect(ctx, objects, warnings); err != nil {
122
+ return err
123
+ }
124
+ claims := map[graphNamespacedName]bool{}
125
+ for _, claim := range (*objects)[claimsStart:] {
126
+ if graphStorageClassName(claim) == name || graphStorageBound(claim, classVolumes[graphClaimVolumeName(claim)]) {
127
+ claims[graphNamespacedName{namespace: claim.GetNamespace(), name: claim.GetName()}] = true
128
+ }
129
+ }
130
+ if len(claims) == 0 {
131
+ return ctx.Err()
132
+ }
133
+ return collectGraphStoragePods(ctx, client, "", claims, objects, warnings)
134
+ }
135
+
136
+ func collectGraphStoragePods(ctx context.Context, client kubernetes.Interface, namespace string, claims map[graphNamespacedName]bool, objects *[]*unstructured.Unstructured, warnings *[]string) error {
137
+ project := func(obj kruntime.Object) (*unstructured.Unstructured, error) {
138
+ pod, ok := obj.(*corev1.Pod)
139
+ if !ok || pod.Namespace == "" || (namespace != "" && pod.Namespace != namespace) {
140
+ return nil, errors.New("storage relationship Pod scope mismatch")
141
+ }
142
+ volumes := []interface{}{}
143
+ for _, volume := range pod.Spec.Volumes {
144
+ if err := ctx.Err(); err != nil {
145
+ return nil, err
146
+ }
147
+ if claim := volume.PersistentVolumeClaim; claim != nil && claims[graphNamespacedName{namespace: pod.Namespace, name: claim.ClaimName}] {
148
+ volumes = append(volumes, map[string]interface{}{"persistentVolumeClaim": map[string]interface{}{"claimName": claim.ClaimName}})
149
+ }
150
+ }
151
+ if len(volumes) == 0 {
152
+ return nil, nil
153
+ }
154
+ projected, err := graphObjectMetadataProjection(pod, "v1", "Pod", false)
155
+ if err != nil {
156
+ return nil, err
157
+ }
158
+ projected.Object["spec"] = map[string]interface{}{"volumes": volumes}
159
+ projected.Object["status"] = map[string]interface{}{"phase": string(pod.Status.Phase)}
160
+ return projected, nil
161
+ }
162
+ return collectGraphSource(ctx, client.CoreV1().Pods(namespace).List, "Pod", project, objects, warnings)
163
+ }