diff --git a/cmd/interlink/main.go b/cmd/interlink/main.go index a658bf9b..b5811657 100644 --- a/cmd/interlink/main.go +++ b/cmd/interlink/main.go @@ -190,6 +190,7 @@ func main() { mutex := http.NewServeMux() mutex.HandleFunc("/status", interLinkAPIs.StatusHandler) mutex.HandleFunc("/create", interLinkAPIs.CreateHandler) + mutex.HandleFunc("/createPodGroup", interLinkAPIs.CreatePodGroupHandler) mutex.HandleFunc("/delete", interLinkAPIs.DeleteHandler) mutex.HandleFunc("/pinglink", interLinkAPIs.Ping) mutex.HandleFunc("/getLogs", interLinkAPIs.GetLogsHandler) diff --git a/cmd/openapi-gen/main.go b/cmd/openapi-gen/main.go index a5c593cf..bfdefa17 100644 --- a/cmd/openapi-gen/main.go +++ b/cmd/openapi-gen/main.go @@ -38,6 +38,20 @@ func main() { panic(err) } + // CREATE POD GROUP + createPodGroupOp, err := reflector.NewOperationContext(http.MethodPost, "/createPodGroup") + if err != nil { + panic(err) + } + + createPodGroupOp.AddReqStructure(new(interlink.PodGroupCreateRequests)) + createPodGroupOp.AddRespStructure(new([]interlink.CreateStruct), func(cu *openapi.ContentUnit) { cu.HTTPStatus = http.StatusOK }) + + err = reflector.AddOperation(createPodGroupOp) + if err != nil { + panic(err) + } + // DELETE deleteOp, err := reflector.NewOperationContext(http.MethodPost, "/delete") if err != nil { diff --git a/docs/docs/guides/02-develop-a-plugin.md b/docs/docs/guides/02-develop-a-plugin.md index 0160d957..48570414 100644 --- a/docs/docs/guides/02-develop-a-plugin.md +++ b/docs/docs/guides/02-develop-a-plugin.md @@ -151,6 +151,26 @@ Retrieves logs from a specific container. **Query Parameters**: Pod UID, container name, log options **Response**: Container logs (plain text) +### POST /createPodGroup (optional) + +Creates the pods of one pod group as a single job. A plugin that implements it +lists `pod-group-create` in the `capabilities` of its ping answer, and it is +used only then; status, logs and delete stay per pod. + +**Request Body**: `RetrievedPodGroupData` **Response**: `List[CreateStruct]`, +one per pod in the order of the request, all with the job ID of the group + +```json +{ + "groupUID": "workload-uid", + "pods": [...] // each pod as sent to /create, in rank order +} +``` + +The order of the pods gives their ranks; rank 0 is the head. Answer 400 with a +text body for a bad request or a permanent refusal, and 503 when the group +cannot be taken now. + ## Developing with the Python SDK ### Basic Plugin Structure diff --git a/docs/openapi/interlink-openapi.json b/docs/openapi/interlink-openapi.json index 70603242..fd1881e8 100644 --- a/docs/openapi/interlink-openapi.json +++ b/docs/openapi/interlink-openapi.json @@ -1 +1 @@ -{"openapi":"3.0.3","info":{"title":"interLink server API","description":"This is the API spec for the Virtual Kubelet to interLink API server communication","version":"0.4.0"},"paths":{"/create":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InterlinkPodCreateRequests"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InterlinkRetrievedPodData"}}}}}}},"/delete":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1Pod"}}}},"responses":{"200":{"description":"OK"}}}},"/getLogs":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InterlinkLogStruct"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}},"/pinglink":{"post":{"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InterlinkPingResponse"}}}}}}},"/status":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/V1Pod"},"nullable":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkPodStatus"}}}}}}}}},"components":{"schemas":{"InterlinkAcceleratorResponse":{"type":"object","properties":{"available":{"type":"string"},"resourceType":{"type":"string"}}},"InterlinkApptainerOptions":{"type":"object","properties":{"cleanenv":{"type":"boolean"},"containall":{"type":"boolean"},"executable":{"type":"string"},"fakeroot":{"type":"boolean"},"fuseMode":{"type":"string"},"noHome":{"type":"boolean"},"noInit":{"type":"boolean"},"noPrivs":{"type":"boolean"},"nvidiaSupport":{"type":"boolean"},"unsquash":{"type":"boolean"}}},"InterlinkContainerLogOpts":{"type":"object","properties":{"Bytes":{"type":"integer"},"Follow":{"type":"boolean"},"Previous":{"type":"boolean"},"SinceSeconds":{"type":"integer"},"SinceTime":{"type":"string","format":"date-time"},"Tail":{"type":"integer"},"Timestamps":{"type":"boolean"}}},"InterlinkLogStruct":{"type":"object","properties":{"ContainerName":{"type":"string"},"Namespace":{"type":"string"},"Opts":{"$ref":"#/components/schemas/InterlinkContainerLogOpts"},"PodName":{"type":"string"},"PodUID":{"type":"string"}}},"InterlinkPingResponse":{"type":"object","properties":{"resources":{"$ref":"#/components/schemas/InterlinkResourcesResponse"},"status":{"type":"string"},"taints":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkTaintResponse"},"nullable":true}}},"InterlinkPodCreateRequests":{"type":"object","properties":{"configmaps":{"type":"array","items":{"$ref":"#/components/schemas/V1ConfigMap"},"nullable":true},"jobscriptURL":{"type":"string"},"pod":{"$ref":"#/components/schemas/V1Pod"},"projectedvolumesmaps":{"type":"array","items":{"$ref":"#/components/schemas/V1ConfigMap"},"nullable":true},"secrets":{"type":"array","items":{"$ref":"#/components/schemas/V1Secret"},"nullable":true}}},"InterlinkPodStatus":{"type":"object","properties":{"JID":{"type":"string"},"UID":{"type":"string"},"containers":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"},"nullable":true},"initContainers":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"},"nullable":true},"name":{"type":"string"},"namespace":{"type":"string"},"nodeName":{"type":"string"},"socketDir":{"type":"string"}}},"InterlinkResourcesResponse":{"type":"object","properties":{"accelerators":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkAcceleratorResponse"}},"cpu":{"type":"string"},"memory":{"type":"string"},"pods":{"type":"string"}}},"InterlinkRetrievedContainer":{"type":"object","properties":{"configMaps":{"type":"array","items":{"$ref":"#/components/schemas/V1ConfigMap"},"nullable":true},"emptyDirs":{"type":"array","items":{"type":"string"},"nullable":true},"name":{"type":"string"},"projectedvolumemaps":{"type":"array","items":{"$ref":"#/components/schemas/V1ConfigMap"},"nullable":true},"secrets":{"type":"array","items":{"$ref":"#/components/schemas/V1Secret"},"nullable":true}}},"InterlinkRetrievedPodData":{"type":"object","properties":{"container":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkRetrievedContainer"},"nullable":true},"jobConfig":{"$ref":"#/components/schemas/InterlinkScriptBuildConfig"},"jobScript":{"type":"string"},"pod":{"$ref":"#/components/schemas/V1Pod"}}},"InterlinkScriptBuildConfig":{"type":"object","properties":{"ApptainerOptions":{"$ref":"#/components/schemas/InterlinkApptainerOptions"},"SingularityHubProxy":{"$ref":"#/components/schemas/InterlinkSingularityHubConfig"},"Volumes":{"$ref":"#/components/schemas/InterlinkVolumesOptions"}}},"InterlinkSingularityHubConfig":{"type":"object","properties":{"cache_validity_seconds":{"type":"integer"},"master_token":{"type":"string"},"server":{"type":"string"}}},"InterlinkTaintResponse":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"InterlinkVolumesOptions":{"type":"object","properties":{"additional_directories_in_path":{"type":"array","items":{"type":"string"},"nullable":true},"apptainer_cachedir":{"type":"string"},"fuse_sleep_seconds":{"type":"integer"},"image_dir":{"type":"string"},"scratch_area":{"type":"string"}}},"IntstrIntOrString":{"type":"object"},"ResourceQuantity":{"type":"object"},"V1AWSElasticBlockStoreVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"partition":{"type":"integer"},"readOnly":{"type":"boolean"},"volumeID":{"type":"string"}}},"V1Affinity":{"type":"object","properties":{"nodeAffinity":{"$ref":"#/components/schemas/V1NodeAffinity"},"podAffinity":{"$ref":"#/components/schemas/V1PodAffinity"},"podAntiAffinity":{"$ref":"#/components/schemas/V1PodAntiAffinity"}}},"V1AppArmorProfile":{"type":"object","properties":{"localhostProfile":{"type":"string","nullable":true},"type":{"type":"string"}}},"V1AzureDiskVolumeSource":{"type":"object","properties":{"cachingMode":{"type":"string","nullable":true},"diskName":{"type":"string"},"diskURI":{"type":"string"},"fsType":{"type":"string","nullable":true},"kind":{"type":"string","nullable":true},"readOnly":{"type":"boolean","nullable":true}}},"V1AzureFileVolumeSource":{"type":"object","properties":{"readOnly":{"type":"boolean"},"secretName":{"type":"string"},"shareName":{"type":"string"}}},"V1CSIVolumeSource":{"type":"object","properties":{"driver":{"type":"string"},"fsType":{"type":"string","nullable":true},"nodePublishSecretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"readOnly":{"type":"boolean","nullable":true},"volumeAttributes":{"type":"object","additionalProperties":{"type":"string"}}}},"V1Capabilities":{"type":"object","properties":{"add":{"type":"array","items":{"type":"string"}},"drop":{"type":"array","items":{"type":"string"}}}},"V1CephFSVolumeSource":{"type":"object","properties":{"monitors":{"type":"array","items":{"type":"string"},"nullable":true},"path":{"type":"string"},"readOnly":{"type":"boolean"},"secretFile":{"type":"string"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"user":{"type":"string"}}},"V1CinderVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"volumeID":{"type":"string"}}},"V1ClusterTrustBundleProjection":{"type":"object","properties":{"labelSelector":{"$ref":"#/components/schemas/V1LabelSelector"},"name":{"type":"string","nullable":true},"optional":{"type":"boolean","nullable":true},"path":{"type":"string"},"signerName":{"type":"string","nullable":true}}},"V1ConfigMap":{"type":"object","properties":{"apiVersion":{"type":"string"},"binaryData":{"type":"object","additionalProperties":{"type":"string","format":"base64"}},"data":{"type":"object","additionalProperties":{"type":"string"}},"immutable":{"type":"boolean","nullable":true},"kind":{"type":"string"},"metadata":{"$ref":"#/components/schemas/V1ObjectMeta"}}},"V1ConfigMapEnvSource":{"type":"object","properties":{"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1ConfigMapKeySelector":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1ConfigMapProjection":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/V1KeyToPath"}},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1ConfigMapVolumeSource":{"type":"object","properties":{"defaultMode":{"type":"integer","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/V1KeyToPath"}},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1Container":{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"command":{"type":"array","items":{"type":"string"}},"env":{"type":"array","items":{"$ref":"#/components/schemas/V1EnvVar"}},"envFrom":{"type":"array","items":{"$ref":"#/components/schemas/V1EnvFromSource"}},"image":{"type":"string"},"imagePullPolicy":{"type":"string"},"lifecycle":{"$ref":"#/components/schemas/V1Lifecycle"},"livenessProbe":{"$ref":"#/components/schemas/V1Probe"},"name":{"type":"string"},"ports":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerPort"}},"readinessProbe":{"$ref":"#/components/schemas/V1Probe"},"resizePolicy":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerResizePolicy"}},"resources":{"$ref":"#/components/schemas/V1ResourceRequirements"},"restartPolicy":{"type":"string","nullable":true},"securityContext":{"$ref":"#/components/schemas/V1SecurityContext"},"startupProbe":{"$ref":"#/components/schemas/V1Probe"},"stdin":{"type":"boolean"},"stdinOnce":{"type":"boolean"},"terminationMessagePath":{"type":"string"},"terminationMessagePolicy":{"type":"string"},"tty":{"type":"boolean"},"volumeDevices":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeDevice"}},"volumeMounts":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeMount"}},"workingDir":{"type":"string"}}},"V1ContainerPort":{"type":"object","properties":{"containerPort":{"type":"integer"},"hostIP":{"type":"string"},"hostPort":{"type":"integer"},"name":{"type":"string"},"protocol":{"type":"string"}}},"V1ContainerResizePolicy":{"type":"object","properties":{"resourceName":{"type":"string"},"restartPolicy":{"type":"string"}}},"V1ContainerState":{"type":"object","properties":{"running":{"$ref":"#/components/schemas/V1ContainerStateRunning"},"terminated":{"$ref":"#/components/schemas/V1ContainerStateTerminated"},"waiting":{"$ref":"#/components/schemas/V1ContainerStateWaiting"}}},"V1ContainerStateRunning":{"type":"object","properties":{"startedAt":{"type":"string"}}},"V1ContainerStateTerminated":{"type":"object","properties":{"containerID":{"type":"string"},"exitCode":{"type":"integer"},"finishedAt":{"type":"string"},"message":{"type":"string"},"reason":{"type":"string"},"signal":{"type":"integer"},"startedAt":{"type":"string"}}},"V1ContainerStateWaiting":{"type":"object","properties":{"message":{"type":"string"},"reason":{"type":"string"}}},"V1ContainerStatus":{"type":"object","properties":{"allocatedResources":{"$ref":"#/components/schemas/V1ResourceList"},"allocatedResourcesStatus":{"type":"array","items":{"$ref":"#/components/schemas/V1ResourceStatus"}},"containerID":{"type":"string"},"image":{"type":"string"},"imageID":{"type":"string"},"lastState":{"$ref":"#/components/schemas/V1ContainerState"},"name":{"type":"string"},"ready":{"type":"boolean"},"resources":{"$ref":"#/components/schemas/V1ResourceRequirements"},"restartCount":{"type":"integer"},"started":{"type":"boolean","nullable":true},"state":{"$ref":"#/components/schemas/V1ContainerState"},"stopSignal":{"type":"string","nullable":true},"user":{"$ref":"#/components/schemas/V1ContainerUser"},"volumeMounts":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeMountStatus"}}}},"V1ContainerUser":{"type":"object","properties":{"linux":{"$ref":"#/components/schemas/V1LinuxContainerUser"}}},"V1DownwardAPIProjection":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/V1DownwardAPIVolumeFile"}}}},"V1DownwardAPIVolumeFile":{"type":"object","properties":{"fieldRef":{"$ref":"#/components/schemas/V1ObjectFieldSelector"},"mode":{"type":"integer","nullable":true},"path":{"type":"string"},"resourceFieldRef":{"$ref":"#/components/schemas/V1ResourceFieldSelector"}}},"V1DownwardAPIVolumeSource":{"type":"object","properties":{"defaultMode":{"type":"integer","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/V1DownwardAPIVolumeFile"}}}},"V1EmptyDirVolumeSource":{"type":"object","properties":{"medium":{"type":"string"},"sizeLimit":{"$ref":"#/components/schemas/ResourceQuantity"}}},"V1EnvFromSource":{"type":"object","properties":{"configMapRef":{"$ref":"#/components/schemas/V1ConfigMapEnvSource"},"prefix":{"type":"string"},"secretRef":{"$ref":"#/components/schemas/V1SecretEnvSource"}}},"V1EnvVar":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"},"valueFrom":{"$ref":"#/components/schemas/V1EnvVarSource"}}},"V1EnvVarSource":{"type":"object","properties":{"configMapKeyRef":{"$ref":"#/components/schemas/V1ConfigMapKeySelector"},"fieldRef":{"$ref":"#/components/schemas/V1ObjectFieldSelector"},"resourceFieldRef":{"$ref":"#/components/schemas/V1ResourceFieldSelector"},"secretKeyRef":{"$ref":"#/components/schemas/V1SecretKeySelector"}}},"V1EphemeralContainer":{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"command":{"type":"array","items":{"type":"string"}},"env":{"type":"array","items":{"$ref":"#/components/schemas/V1EnvVar"}},"envFrom":{"type":"array","items":{"$ref":"#/components/schemas/V1EnvFromSource"}},"image":{"type":"string"},"imagePullPolicy":{"type":"string"},"lifecycle":{"$ref":"#/components/schemas/V1Lifecycle"},"livenessProbe":{"$ref":"#/components/schemas/V1Probe"},"name":{"type":"string"},"ports":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerPort"}},"readinessProbe":{"$ref":"#/components/schemas/V1Probe"},"resizePolicy":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerResizePolicy"}},"resources":{"$ref":"#/components/schemas/V1ResourceRequirements"},"restartPolicy":{"type":"string","nullable":true},"securityContext":{"$ref":"#/components/schemas/V1SecurityContext"},"startupProbe":{"$ref":"#/components/schemas/V1Probe"},"stdin":{"type":"boolean"},"stdinOnce":{"type":"boolean"},"targetContainerName":{"type":"string"},"terminationMessagePath":{"type":"string"},"terminationMessagePolicy":{"type":"string"},"tty":{"type":"boolean"},"volumeDevices":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeDevice"}},"volumeMounts":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeMount"}},"workingDir":{"type":"string"}}},"V1EphemeralVolumeSource":{"type":"object","properties":{"volumeClaimTemplate":{"$ref":"#/components/schemas/V1PersistentVolumeClaimTemplate"}}},"V1ExecAction":{"type":"object","properties":{"command":{"type":"array","items":{"type":"string"}}}},"V1FCVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"lun":{"type":"integer","nullable":true},"readOnly":{"type":"boolean"},"targetWWNs":{"type":"array","items":{"type":"string"}},"wwids":{"type":"array","items":{"type":"string"}}}},"V1FieldsV1":{"type":"object"},"V1FlexVolumeSource":{"type":"object","properties":{"driver":{"type":"string"},"fsType":{"type":"string"},"options":{"type":"object","additionalProperties":{"type":"string"}},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"}}},"V1FlockerVolumeSource":{"type":"object","properties":{"datasetName":{"type":"string"},"datasetUUID":{"type":"string"}}},"V1GCEPersistentDiskVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"partition":{"type":"integer"},"pdName":{"type":"string"},"readOnly":{"type":"boolean"}}},"V1GRPCAction":{"type":"object","properties":{"port":{"type":"integer"},"service":{"type":"string","nullable":true}}},"V1GitRepoVolumeSource":{"type":"object","properties":{"directory":{"type":"string"},"repository":{"type":"string"},"revision":{"type":"string"}}},"V1GlusterfsVolumeSource":{"type":"object","properties":{"endpoints":{"type":"string"},"path":{"type":"string"},"readOnly":{"type":"boolean"}}},"V1HTTPGetAction":{"type":"object","properties":{"host":{"type":"string"},"httpHeaders":{"type":"array","items":{"$ref":"#/components/schemas/V1HTTPHeader"}},"path":{"type":"string"},"port":{"$ref":"#/components/schemas/IntstrIntOrString"},"scheme":{"type":"string"}}},"V1HTTPHeader":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"V1HostAlias":{"type":"object","properties":{"hostnames":{"type":"array","items":{"type":"string"}},"ip":{"type":"string"}}},"V1HostIP":{"type":"object","properties":{"ip":{"type":"string"}}},"V1HostPathVolumeSource":{"type":"object","properties":{"path":{"type":"string"},"type":{"type":"string","nullable":true}}},"V1ISCSIVolumeSource":{"type":"object","properties":{"chapAuthDiscovery":{"type":"boolean"},"chapAuthSession":{"type":"boolean"},"fsType":{"type":"string"},"initiatorName":{"type":"string","nullable":true},"iqn":{"type":"string"},"iscsiInterface":{"type":"string"},"lun":{"type":"integer"},"portals":{"type":"array","items":{"type":"string"}},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"targetPortal":{"type":"string"}}},"V1ImageVolumeSource":{"type":"object","properties":{"pullPolicy":{"type":"string"},"reference":{"type":"string"}}},"V1KeyToPath":{"type":"object","properties":{"key":{"type":"string"},"mode":{"type":"integer","nullable":true},"path":{"type":"string"}}},"V1LabelSelector":{"type":"object","properties":{"matchExpressions":{"type":"array","items":{"$ref":"#/components/schemas/V1LabelSelectorRequirement"}},"matchLabels":{"type":"object","additionalProperties":{"type":"string"}}}},"V1LabelSelectorRequirement":{"type":"object","properties":{"key":{"type":"string"},"operator":{"type":"string"},"values":{"type":"array","items":{"type":"string"}}}},"V1Lifecycle":{"type":"object","properties":{"postStart":{"$ref":"#/components/schemas/V1LifecycleHandler"},"preStop":{"$ref":"#/components/schemas/V1LifecycleHandler"},"stopSignal":{"type":"string","nullable":true}}},"V1LifecycleHandler":{"type":"object","properties":{"exec":{"$ref":"#/components/schemas/V1ExecAction"},"httpGet":{"$ref":"#/components/schemas/V1HTTPGetAction"},"sleep":{"$ref":"#/components/schemas/V1SleepAction"},"tcpSocket":{"$ref":"#/components/schemas/V1TCPSocketAction"}}},"V1LinuxContainerUser":{"type":"object","properties":{"gid":{"type":"integer"},"supplementalGroups":{"type":"array","items":{"type":"integer"}},"uid":{"type":"integer"}}},"V1LocalObjectReference":{"type":"object","properties":{"name":{"type":"string"}}},"V1ManagedFieldsEntry":{"type":"object","properties":{"apiVersion":{"type":"string"},"fieldsType":{"type":"string"},"fieldsV1":{"$ref":"#/components/schemas/V1FieldsV1"},"manager":{"type":"string"},"operation":{"type":"string"},"subresource":{"type":"string"},"time":{"type":"string"}}},"V1NFSVolumeSource":{"type":"object","properties":{"path":{"type":"string"},"readOnly":{"type":"boolean"},"server":{"type":"string"}}},"V1NodeAffinity":{"type":"object","properties":{"preferredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1PreferredSchedulingTerm"}},"requiredDuringSchedulingIgnoredDuringExecution":{"$ref":"#/components/schemas/V1NodeSelector"}}},"V1NodeSelector":{"type":"object","properties":{"nodeSelectorTerms":{"type":"array","items":{"$ref":"#/components/schemas/V1NodeSelectorTerm"},"nullable":true}}},"V1NodeSelectorRequirement":{"type":"object","properties":{"key":{"type":"string"},"operator":{"type":"string"},"values":{"type":"array","items":{"type":"string"}}}},"V1NodeSelectorTerm":{"type":"object","properties":{"matchExpressions":{"type":"array","items":{"$ref":"#/components/schemas/V1NodeSelectorRequirement"}},"matchFields":{"type":"array","items":{"$ref":"#/components/schemas/V1NodeSelectorRequirement"}}}},"V1ObjectFieldSelector":{"type":"object","properties":{"apiVersion":{"type":"string"},"fieldPath":{"type":"string"}}},"V1ObjectMeta":{"type":"object","properties":{"annotations":{"type":"object","additionalProperties":{"type":"string"}},"creationTimestamp":{"type":"string"},"deletionGracePeriodSeconds":{"type":"integer","nullable":true},"deletionTimestamp":{"type":"string"},"finalizers":{"type":"array","items":{"type":"string"}},"generateName":{"type":"string"},"generation":{"type":"integer"},"labels":{"type":"object","additionalProperties":{"type":"string"}},"managedFields":{"type":"array","items":{"$ref":"#/components/schemas/V1ManagedFieldsEntry"}},"name":{"type":"string"},"namespace":{"type":"string"},"ownerReferences":{"type":"array","items":{"$ref":"#/components/schemas/V1OwnerReference"}},"resourceVersion":{"type":"string"},"selfLink":{"type":"string"},"uid":{"type":"string"}}},"V1OwnerReference":{"type":"object","properties":{"apiVersion":{"type":"string"},"blockOwnerDeletion":{"type":"boolean","nullable":true},"controller":{"type":"boolean","nullable":true},"kind":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}}},"V1PersistentVolumeClaimSpec":{"type":"object","properties":{"accessModes":{"type":"array","items":{"type":"string"}},"dataSource":{"$ref":"#/components/schemas/V1TypedLocalObjectReference"},"dataSourceRef":{"$ref":"#/components/schemas/V1TypedObjectReference"},"resources":{"$ref":"#/components/schemas/V1VolumeResourceRequirements"},"selector":{"$ref":"#/components/schemas/V1LabelSelector"},"storageClassName":{"type":"string","nullable":true},"volumeAttributesClassName":{"type":"string","nullable":true},"volumeMode":{"type":"string","nullable":true},"volumeName":{"type":"string"}}},"V1PersistentVolumeClaimTemplate":{"type":"object","properties":{"metadata":{"$ref":"#/components/schemas/V1ObjectMeta"},"spec":{"$ref":"#/components/schemas/V1PersistentVolumeClaimSpec"}}},"V1PersistentVolumeClaimVolumeSource":{"type":"object","properties":{"claimName":{"type":"string"},"readOnly":{"type":"boolean"}}},"V1PhotonPersistentDiskVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"pdID":{"type":"string"}}},"V1Pod":{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/components/schemas/V1ObjectMeta"},"spec":{"$ref":"#/components/schemas/V1PodSpec"},"status":{"$ref":"#/components/schemas/V1PodStatus"}}},"V1PodAffinity":{"type":"object","properties":{"preferredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1WeightedPodAffinityTerm"}},"requiredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1PodAffinityTerm"}}}},"V1PodAffinityTerm":{"type":"object","properties":{"labelSelector":{"$ref":"#/components/schemas/V1LabelSelector"},"matchLabelKeys":{"type":"array","items":{"type":"string"}},"mismatchLabelKeys":{"type":"array","items":{"type":"string"}},"namespaceSelector":{"$ref":"#/components/schemas/V1LabelSelector"},"namespaces":{"type":"array","items":{"type":"string"}},"topologyKey":{"type":"string"}}},"V1PodAntiAffinity":{"type":"object","properties":{"preferredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1WeightedPodAffinityTerm"}},"requiredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1PodAffinityTerm"}}}},"V1PodCondition":{"type":"object","properties":{"lastProbeTime":{"type":"string"},"lastTransitionTime":{"type":"string"},"message":{"type":"string"},"observedGeneration":{"type":"integer"},"reason":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"}}},"V1PodDNSConfig":{"type":"object","properties":{"nameservers":{"type":"array","items":{"type":"string"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/V1PodDNSConfigOption"}},"searches":{"type":"array","items":{"type":"string"}}}},"V1PodDNSConfigOption":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string","nullable":true}}},"V1PodIP":{"type":"object","properties":{"ip":{"type":"string"}}},"V1PodOS":{"type":"object","properties":{"name":{"type":"string"}}},"V1PodReadinessGate":{"type":"object","properties":{"conditionType":{"type":"string"}}},"V1PodResourceClaim":{"type":"object","properties":{"name":{"type":"string"},"resourceClaimName":{"type":"string","nullable":true},"resourceClaimTemplateName":{"type":"string","nullable":true}}},"V1PodResourceClaimStatus":{"type":"object","properties":{"name":{"type":"string"},"resourceClaimName":{"type":"string","nullable":true}}},"V1PodSchedulingGate":{"type":"object","properties":{"name":{"type":"string"}}},"V1PodSecurityContext":{"type":"object","properties":{"appArmorProfile":{"$ref":"#/components/schemas/V1AppArmorProfile"},"fsGroup":{"type":"integer","nullable":true},"fsGroupChangePolicy":{"type":"string","nullable":true},"runAsGroup":{"type":"integer","nullable":true},"runAsNonRoot":{"type":"boolean","nullable":true},"runAsUser":{"type":"integer","nullable":true},"seLinuxChangePolicy":{"type":"string","nullable":true},"seLinuxOptions":{"$ref":"#/components/schemas/V1SELinuxOptions"},"seccompProfile":{"$ref":"#/components/schemas/V1SeccompProfile"},"supplementalGroups":{"type":"array","items":{"type":"integer"}},"supplementalGroupsPolicy":{"type":"string","nullable":true},"sysctls":{"type":"array","items":{"$ref":"#/components/schemas/V1Sysctl"}},"windowsOptions":{"$ref":"#/components/schemas/V1WindowsSecurityContextOptions"}}},"V1PodSpec":{"type":"object","properties":{"activeDeadlineSeconds":{"type":"integer","nullable":true},"affinity":{"$ref":"#/components/schemas/V1Affinity"},"automountServiceAccountToken":{"type":"boolean","nullable":true},"containers":{"type":"array","items":{"$ref":"#/components/schemas/V1Container"},"nullable":true},"dnsConfig":{"$ref":"#/components/schemas/V1PodDNSConfig"},"dnsPolicy":{"type":"string"},"enableServiceLinks":{"type":"boolean","nullable":true},"ephemeralContainers":{"type":"array","items":{"$ref":"#/components/schemas/V1EphemeralContainer"}},"hostAliases":{"type":"array","items":{"$ref":"#/components/schemas/V1HostAlias"}},"hostIPC":{"type":"boolean"},"hostNetwork":{"type":"boolean"},"hostPID":{"type":"boolean"},"hostUsers":{"type":"boolean","nullable":true},"hostname":{"type":"string"},"imagePullSecrets":{"type":"array","items":{"$ref":"#/components/schemas/V1LocalObjectReference"}},"initContainers":{"type":"array","items":{"$ref":"#/components/schemas/V1Container"}},"nodeName":{"type":"string"},"nodeSelector":{"type":"object","additionalProperties":{"type":"string"}},"os":{"$ref":"#/components/schemas/V1PodOS"},"overhead":{"$ref":"#/components/schemas/V1ResourceList"},"preemptionPolicy":{"type":"string","nullable":true},"priority":{"type":"integer","nullable":true},"priorityClassName":{"type":"string"},"readinessGates":{"type":"array","items":{"$ref":"#/components/schemas/V1PodReadinessGate"}},"resourceClaims":{"type":"array","items":{"$ref":"#/components/schemas/V1PodResourceClaim"}},"resources":{"$ref":"#/components/schemas/V1ResourceRequirements"},"restartPolicy":{"type":"string"},"runtimeClassName":{"type":"string","nullable":true},"schedulerName":{"type":"string"},"schedulingGates":{"type":"array","items":{"$ref":"#/components/schemas/V1PodSchedulingGate"}},"securityContext":{"$ref":"#/components/schemas/V1PodSecurityContext"},"serviceAccount":{"type":"string"},"serviceAccountName":{"type":"string"},"setHostnameAsFQDN":{"type":"boolean","nullable":true},"shareProcessNamespace":{"type":"boolean","nullable":true},"subdomain":{"type":"string"},"terminationGracePeriodSeconds":{"type":"integer","nullable":true},"tolerations":{"type":"array","items":{"$ref":"#/components/schemas/V1Toleration"}},"topologySpreadConstraints":{"type":"array","items":{"$ref":"#/components/schemas/V1TopologySpreadConstraint"}},"volumes":{"type":"array","items":{"$ref":"#/components/schemas/V1Volume"}}}},"V1PodStatus":{"type":"object","properties":{"conditions":{"type":"array","items":{"$ref":"#/components/schemas/V1PodCondition"}},"containerStatuses":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"}},"ephemeralContainerStatuses":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"}},"hostIP":{"type":"string"},"hostIPs":{"type":"array","items":{"$ref":"#/components/schemas/V1HostIP"}},"initContainerStatuses":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"}},"message":{"type":"string"},"nominatedNodeName":{"type":"string"},"observedGeneration":{"type":"integer"},"phase":{"type":"string"},"podIP":{"type":"string"},"podIPs":{"type":"array","items":{"$ref":"#/components/schemas/V1PodIP"}},"qosClass":{"type":"string"},"reason":{"type":"string"},"resize":{"type":"string"},"resourceClaimStatuses":{"type":"array","items":{"$ref":"#/components/schemas/V1PodResourceClaimStatus"}},"startTime":{"type":"string"}}},"V1PortworxVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"readOnly":{"type":"boolean"},"volumeID":{"type":"string"}}},"V1PreferredSchedulingTerm":{"type":"object","properties":{"preference":{"$ref":"#/components/schemas/V1NodeSelectorTerm"},"weight":{"type":"integer"}}},"V1Probe":{"type":"object","properties":{"exec":{"$ref":"#/components/schemas/V1ExecAction"},"failureThreshold":{"type":"integer"},"grpc":{"$ref":"#/components/schemas/V1GRPCAction"},"httpGet":{"$ref":"#/components/schemas/V1HTTPGetAction"},"initialDelaySeconds":{"type":"integer"},"periodSeconds":{"type":"integer"},"successThreshold":{"type":"integer"},"tcpSocket":{"$ref":"#/components/schemas/V1TCPSocketAction"},"terminationGracePeriodSeconds":{"type":"integer","nullable":true},"timeoutSeconds":{"type":"integer"}}},"V1ProjectedVolumeSource":{"type":"object","properties":{"defaultMode":{"type":"integer","nullable":true},"sources":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeProjection"},"nullable":true}}},"V1QuobyteVolumeSource":{"type":"object","properties":{"group":{"type":"string"},"readOnly":{"type":"boolean"},"registry":{"type":"string"},"tenant":{"type":"string"},"user":{"type":"string"},"volume":{"type":"string"}}},"V1RBDVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"image":{"type":"string"},"keyring":{"type":"string"},"monitors":{"type":"array","items":{"type":"string"},"nullable":true},"pool":{"type":"string"},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"user":{"type":"string"}}},"V1ResourceClaim":{"type":"object","properties":{"name":{"type":"string"},"request":{"type":"string"}}},"V1ResourceFieldSelector":{"type":"object","properties":{"containerName":{"type":"string"},"divisor":{"$ref":"#/components/schemas/ResourceQuantity"},"resource":{"type":"string"}}},"V1ResourceHealth":{"type":"object","properties":{"health":{"type":"string"},"resourceID":{"type":"string"}}},"V1ResourceList":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ResourceQuantity"}},"V1ResourceRequirements":{"type":"object","properties":{"claims":{"type":"array","items":{"$ref":"#/components/schemas/V1ResourceClaim"}},"limits":{"$ref":"#/components/schemas/V1ResourceList"},"requests":{"$ref":"#/components/schemas/V1ResourceList"}}},"V1ResourceStatus":{"type":"object","properties":{"name":{"type":"string"},"resources":{"type":"array","items":{"$ref":"#/components/schemas/V1ResourceHealth"}}}},"V1SELinuxOptions":{"type":"object","properties":{"level":{"type":"string"},"role":{"type":"string"},"type":{"type":"string"},"user":{"type":"string"}}},"V1ScaleIOVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"gateway":{"type":"string"},"protectionDomain":{"type":"string"},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"sslEnabled":{"type":"boolean"},"storageMode":{"type":"string"},"storagePool":{"type":"string"},"system":{"type":"string"},"volumeName":{"type":"string"}}},"V1SeccompProfile":{"type":"object","properties":{"localhostProfile":{"type":"string","nullable":true},"type":{"type":"string"}}},"V1Secret":{"type":"object","properties":{"apiVersion":{"type":"string"},"data":{"type":"object","additionalProperties":{"type":"string","format":"base64"}},"immutable":{"type":"boolean","nullable":true},"kind":{"type":"string"},"metadata":{"$ref":"#/components/schemas/V1ObjectMeta"},"stringData":{"type":"object","additionalProperties":{"type":"string"}},"type":{"type":"string"}}},"V1SecretEnvSource":{"type":"object","properties":{"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1SecretKeySelector":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1SecretProjection":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/V1KeyToPath"}},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1SecretVolumeSource":{"type":"object","properties":{"defaultMode":{"type":"integer","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/V1KeyToPath"}},"optional":{"type":"boolean","nullable":true},"secretName":{"type":"string"}}},"V1SecurityContext":{"type":"object","properties":{"allowPrivilegeEscalation":{"type":"boolean","nullable":true},"appArmorProfile":{"$ref":"#/components/schemas/V1AppArmorProfile"},"capabilities":{"$ref":"#/components/schemas/V1Capabilities"},"privileged":{"type":"boolean","nullable":true},"procMount":{"type":"string","nullable":true},"readOnlyRootFilesystem":{"type":"boolean","nullable":true},"runAsGroup":{"type":"integer","nullable":true},"runAsNonRoot":{"type":"boolean","nullable":true},"runAsUser":{"type":"integer","nullable":true},"seLinuxOptions":{"$ref":"#/components/schemas/V1SELinuxOptions"},"seccompProfile":{"$ref":"#/components/schemas/V1SeccompProfile"},"windowsOptions":{"$ref":"#/components/schemas/V1WindowsSecurityContextOptions"}}},"V1ServiceAccountTokenProjection":{"type":"object","properties":{"audience":{"type":"string"},"expirationSeconds":{"type":"integer","nullable":true},"path":{"type":"string"}}},"V1SleepAction":{"type":"object","properties":{"seconds":{"type":"integer"}}},"V1StorageOSVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"volumeName":{"type":"string"},"volumeNamespace":{"type":"string"}}},"V1Sysctl":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"V1TCPSocketAction":{"type":"object","properties":{"host":{"type":"string"},"port":{"$ref":"#/components/schemas/IntstrIntOrString"}}},"V1Toleration":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"operator":{"type":"string"},"tolerationSeconds":{"type":"integer","nullable":true},"value":{"type":"string"}}},"V1TopologySpreadConstraint":{"type":"object","properties":{"labelSelector":{"$ref":"#/components/schemas/V1LabelSelector"},"matchLabelKeys":{"type":"array","items":{"type":"string"}},"maxSkew":{"type":"integer"},"minDomains":{"type":"integer","nullable":true},"nodeAffinityPolicy":{"type":"string","nullable":true},"nodeTaintsPolicy":{"type":"string","nullable":true},"topologyKey":{"type":"string"},"whenUnsatisfiable":{"type":"string"}}},"V1TypedLocalObjectReference":{"type":"object","properties":{"apiGroup":{"type":"string","nullable":true},"kind":{"type":"string"},"name":{"type":"string"}}},"V1TypedObjectReference":{"type":"object","properties":{"apiGroup":{"type":"string","nullable":true},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string","nullable":true}}},"V1Volume":{"type":"object","properties":{"awsElasticBlockStore":{"$ref":"#/components/schemas/V1AWSElasticBlockStoreVolumeSource"},"azureDisk":{"$ref":"#/components/schemas/V1AzureDiskVolumeSource"},"azureFile":{"$ref":"#/components/schemas/V1AzureFileVolumeSource"},"cephfs":{"$ref":"#/components/schemas/V1CephFSVolumeSource"},"cinder":{"$ref":"#/components/schemas/V1CinderVolumeSource"},"configMap":{"$ref":"#/components/schemas/V1ConfigMapVolumeSource"},"csi":{"$ref":"#/components/schemas/V1CSIVolumeSource"},"downwardAPI":{"$ref":"#/components/schemas/V1DownwardAPIVolumeSource"},"emptyDir":{"$ref":"#/components/schemas/V1EmptyDirVolumeSource"},"ephemeral":{"$ref":"#/components/schemas/V1EphemeralVolumeSource"},"fc":{"$ref":"#/components/schemas/V1FCVolumeSource"},"flexVolume":{"$ref":"#/components/schemas/V1FlexVolumeSource"},"flocker":{"$ref":"#/components/schemas/V1FlockerVolumeSource"},"gcePersistentDisk":{"$ref":"#/components/schemas/V1GCEPersistentDiskVolumeSource"},"gitRepo":{"$ref":"#/components/schemas/V1GitRepoVolumeSource"},"glusterfs":{"$ref":"#/components/schemas/V1GlusterfsVolumeSource"},"hostPath":{"$ref":"#/components/schemas/V1HostPathVolumeSource"},"image":{"$ref":"#/components/schemas/V1ImageVolumeSource"},"iscsi":{"$ref":"#/components/schemas/V1ISCSIVolumeSource"},"name":{"type":"string"},"nfs":{"$ref":"#/components/schemas/V1NFSVolumeSource"},"persistentVolumeClaim":{"$ref":"#/components/schemas/V1PersistentVolumeClaimVolumeSource"},"photonPersistentDisk":{"$ref":"#/components/schemas/V1PhotonPersistentDiskVolumeSource"},"portworxVolume":{"$ref":"#/components/schemas/V1PortworxVolumeSource"},"projected":{"$ref":"#/components/schemas/V1ProjectedVolumeSource"},"quobyte":{"$ref":"#/components/schemas/V1QuobyteVolumeSource"},"rbd":{"$ref":"#/components/schemas/V1RBDVolumeSource"},"scaleIO":{"$ref":"#/components/schemas/V1ScaleIOVolumeSource"},"secret":{"$ref":"#/components/schemas/V1SecretVolumeSource"},"storageos":{"$ref":"#/components/schemas/V1StorageOSVolumeSource"},"vsphereVolume":{"$ref":"#/components/schemas/V1VsphereVirtualDiskVolumeSource"}}},"V1VolumeDevice":{"type":"object","properties":{"devicePath":{"type":"string"},"name":{"type":"string"}}},"V1VolumeMount":{"type":"object","properties":{"mountPath":{"type":"string"},"mountPropagation":{"type":"string","nullable":true},"name":{"type":"string"},"readOnly":{"type":"boolean"},"recursiveReadOnly":{"type":"string","nullable":true},"subPath":{"type":"string"},"subPathExpr":{"type":"string"}}},"V1VolumeMountStatus":{"type":"object","properties":{"mountPath":{"type":"string"},"name":{"type":"string"},"readOnly":{"type":"boolean"},"recursiveReadOnly":{"type":"string","nullable":true}}},"V1VolumeProjection":{"type":"object","properties":{"clusterTrustBundle":{"$ref":"#/components/schemas/V1ClusterTrustBundleProjection"},"configMap":{"$ref":"#/components/schemas/V1ConfigMapProjection"},"downwardAPI":{"$ref":"#/components/schemas/V1DownwardAPIProjection"},"secret":{"$ref":"#/components/schemas/V1SecretProjection"},"serviceAccountToken":{"$ref":"#/components/schemas/V1ServiceAccountTokenProjection"}}},"V1VolumeResourceRequirements":{"type":"object","properties":{"limits":{"$ref":"#/components/schemas/V1ResourceList"},"requests":{"$ref":"#/components/schemas/V1ResourceList"}}},"V1VsphereVirtualDiskVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"storagePolicyID":{"type":"string"},"storagePolicyName":{"type":"string"},"volumePath":{"type":"string"}}},"V1WeightedPodAffinityTerm":{"type":"object","properties":{"podAffinityTerm":{"$ref":"#/components/schemas/V1PodAffinityTerm"},"weight":{"type":"integer"}}},"V1WindowsSecurityContextOptions":{"type":"object","properties":{"gmsaCredentialSpec":{"type":"string","nullable":true},"gmsaCredentialSpecName":{"type":"string","nullable":true},"hostProcess":{"type":"boolean","nullable":true},"runAsUserName":{"type":"string","nullable":true}}}}}} \ No newline at end of file +{"openapi":"3.0.3","info":{"title":"interLink server API","description":"This is the API spec for the Virtual Kubelet to interLink API server communication","version":"0.4.0"},"paths":{"/create":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InterlinkPodCreateRequests"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InterlinkRetrievedPodData"}}}}}}},"/createPodGroup":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InterlinkPodGroupCreateRequests"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkCreateStruct"}}}}}}}},"/delete":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1Pod"}}}},"responses":{"200":{"description":"OK"}}}},"/getLogs":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InterlinkLogStruct"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}},"/pinglink":{"post":{"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InterlinkPingResponse"}}}}}}},"/status":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/V1Pod"},"nullable":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkPodStatus"}}}}}}}}},"components":{"schemas":{"InterlinkAcceleratorResponse":{"type":"object","properties":{"available":{"type":"string"},"resourceType":{"type":"string"}}},"InterlinkApptainerOptions":{"type":"object","properties":{"cleanenv":{"type":"boolean"},"containall":{"type":"boolean"},"executable":{"type":"string"},"fakeroot":{"type":"boolean"},"fuseMode":{"type":"string"},"noHome":{"type":"boolean"},"noInit":{"type":"boolean"},"noPrivs":{"type":"boolean"},"nvidiaSupport":{"type":"boolean"},"unsquash":{"type":"boolean"}}},"InterlinkContainerLogOpts":{"type":"object","properties":{"Bytes":{"type":"integer"},"Follow":{"type":"boolean"},"Previous":{"type":"boolean"},"SinceSeconds":{"type":"integer"},"SinceTime":{"type":"string","format":"date-time"},"Tail":{"type":"integer"},"Timestamps":{"type":"boolean"}}},"InterlinkCreateStruct":{"type":"object","properties":{"PodJID":{"type":"string"},"PodUID":{"type":"string"}}},"InterlinkLogStruct":{"type":"object","properties":{"ContainerName":{"type":"string"},"Namespace":{"type":"string"},"Opts":{"$ref":"#/components/schemas/InterlinkContainerLogOpts"},"PodName":{"type":"string"},"PodUID":{"type":"string"}}},"InterlinkPingResponse":{"type":"object","properties":{"resources":{"$ref":"#/components/schemas/InterlinkResourcesResponse"},"status":{"type":"string"},"taints":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkTaintResponse"},"nullable":true}}},"InterlinkPodCreateRequests":{"type":"object","properties":{"configmaps":{"type":"array","items":{"$ref":"#/components/schemas/V1ConfigMap"},"nullable":true},"jobscriptURL":{"type":"string"},"pod":{"$ref":"#/components/schemas/V1Pod"},"projectedvolumesmaps":{"type":"array","items":{"$ref":"#/components/schemas/V1ConfigMap"},"nullable":true},"secrets":{"type":"array","items":{"$ref":"#/components/schemas/V1Secret"},"nullable":true}}},"InterlinkPodGroupCreateRequests":{"type":"object","properties":{"groupUID":{"type":"string"},"pods":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkPodCreateRequests"},"nullable":true}}},"InterlinkPodStatus":{"type":"object","properties":{"JID":{"type":"string"},"UID":{"type":"string"},"containers":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"},"nullable":true},"initContainers":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"},"nullable":true},"name":{"type":"string"},"namespace":{"type":"string"},"nodeName":{"type":"string"},"socketDir":{"type":"string"}}},"InterlinkResourcesResponse":{"type":"object","properties":{"accelerators":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkAcceleratorResponse"}},"cpu":{"type":"string"},"memory":{"type":"string"},"pods":{"type":"string"}}},"InterlinkRetrievedContainer":{"type":"object","properties":{"configMaps":{"type":"array","items":{"$ref":"#/components/schemas/V1ConfigMap"},"nullable":true},"emptyDirs":{"type":"array","items":{"type":"string"},"nullable":true},"name":{"type":"string"},"projectedvolumemaps":{"type":"array","items":{"$ref":"#/components/schemas/V1ConfigMap"},"nullable":true},"secrets":{"type":"array","items":{"$ref":"#/components/schemas/V1Secret"},"nullable":true}}},"InterlinkRetrievedPodData":{"type":"object","properties":{"container":{"type":"array","items":{"$ref":"#/components/schemas/InterlinkRetrievedContainer"},"nullable":true},"jobConfig":{"$ref":"#/components/schemas/InterlinkScriptBuildConfig"},"jobScript":{"type":"string"},"pod":{"$ref":"#/components/schemas/V1Pod"}}},"InterlinkScriptBuildConfig":{"type":"object","properties":{"ApptainerOptions":{"$ref":"#/components/schemas/InterlinkApptainerOptions"},"SingularityHubProxy":{"$ref":"#/components/schemas/InterlinkSingularityHubConfig"},"Volumes":{"$ref":"#/components/schemas/InterlinkVolumesOptions"}}},"InterlinkSingularityHubConfig":{"type":"object","properties":{"cache_validity_seconds":{"type":"integer"},"master_token":{"type":"string"},"server":{"type":"string"}}},"InterlinkTaintResponse":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"InterlinkVolumesOptions":{"type":"object","properties":{"additional_directories_in_path":{"type":"array","items":{"type":"string"},"nullable":true},"apptainer_cachedir":{"type":"string"},"fuse_sleep_seconds":{"type":"integer"},"image_dir":{"type":"string"},"scratch_area":{"type":"string"}}},"IntstrIntOrString":{"type":"object"},"ResourceQuantity":{"type":"object"},"V1AWSElasticBlockStoreVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"partition":{"type":"integer"},"readOnly":{"type":"boolean"},"volumeID":{"type":"string"}}},"V1Affinity":{"type":"object","properties":{"nodeAffinity":{"$ref":"#/components/schemas/V1NodeAffinity"},"podAffinity":{"$ref":"#/components/schemas/V1PodAffinity"},"podAntiAffinity":{"$ref":"#/components/schemas/V1PodAntiAffinity"}}},"V1AppArmorProfile":{"type":"object","properties":{"localhostProfile":{"type":"string","nullable":true},"type":{"type":"string"}}},"V1AzureDiskVolumeSource":{"type":"object","properties":{"cachingMode":{"type":"string","nullable":true},"diskName":{"type":"string"},"diskURI":{"type":"string"},"fsType":{"type":"string","nullable":true},"kind":{"type":"string","nullable":true},"readOnly":{"type":"boolean","nullable":true}}},"V1AzureFileVolumeSource":{"type":"object","properties":{"readOnly":{"type":"boolean"},"secretName":{"type":"string"},"shareName":{"type":"string"}}},"V1CSIVolumeSource":{"type":"object","properties":{"driver":{"type":"string"},"fsType":{"type":"string","nullable":true},"nodePublishSecretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"readOnly":{"type":"boolean","nullable":true},"volumeAttributes":{"type":"object","additionalProperties":{"type":"string"}}}},"V1Capabilities":{"type":"object","properties":{"add":{"type":"array","items":{"type":"string"}},"drop":{"type":"array","items":{"type":"string"}}}},"V1CephFSVolumeSource":{"type":"object","properties":{"monitors":{"type":"array","items":{"type":"string"},"nullable":true},"path":{"type":"string"},"readOnly":{"type":"boolean"},"secretFile":{"type":"string"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"user":{"type":"string"}}},"V1CinderVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"volumeID":{"type":"string"}}},"V1ClusterTrustBundleProjection":{"type":"object","properties":{"labelSelector":{"$ref":"#/components/schemas/V1LabelSelector"},"name":{"type":"string","nullable":true},"optional":{"type":"boolean","nullable":true},"path":{"type":"string"},"signerName":{"type":"string","nullable":true}}},"V1ConfigMap":{"type":"object","properties":{"apiVersion":{"type":"string"},"binaryData":{"type":"object","additionalProperties":{"type":"string","format":"base64"}},"data":{"type":"object","additionalProperties":{"type":"string"}},"immutable":{"type":"boolean","nullable":true},"kind":{"type":"string"},"metadata":{"$ref":"#/components/schemas/V1ObjectMeta"}}},"V1ConfigMapEnvSource":{"type":"object","properties":{"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1ConfigMapKeySelector":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1ConfigMapProjection":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/V1KeyToPath"}},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1ConfigMapVolumeSource":{"type":"object","properties":{"defaultMode":{"type":"integer","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/V1KeyToPath"}},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1Container":{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"command":{"type":"array","items":{"type":"string"}},"env":{"type":"array","items":{"$ref":"#/components/schemas/V1EnvVar"}},"envFrom":{"type":"array","items":{"$ref":"#/components/schemas/V1EnvFromSource"}},"image":{"type":"string"},"imagePullPolicy":{"type":"string"},"lifecycle":{"$ref":"#/components/schemas/V1Lifecycle"},"livenessProbe":{"$ref":"#/components/schemas/V1Probe"},"name":{"type":"string"},"ports":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerPort"}},"readinessProbe":{"$ref":"#/components/schemas/V1Probe"},"resizePolicy":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerResizePolicy"}},"resources":{"$ref":"#/components/schemas/V1ResourceRequirements"},"restartPolicy":{"type":"string","nullable":true},"securityContext":{"$ref":"#/components/schemas/V1SecurityContext"},"startupProbe":{"$ref":"#/components/schemas/V1Probe"},"stdin":{"type":"boolean"},"stdinOnce":{"type":"boolean"},"terminationMessagePath":{"type":"string"},"terminationMessagePolicy":{"type":"string"},"tty":{"type":"boolean"},"volumeDevices":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeDevice"}},"volumeMounts":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeMount"}},"workingDir":{"type":"string"}}},"V1ContainerPort":{"type":"object","properties":{"containerPort":{"type":"integer"},"hostIP":{"type":"string"},"hostPort":{"type":"integer"},"name":{"type":"string"},"protocol":{"type":"string"}}},"V1ContainerResizePolicy":{"type":"object","properties":{"resourceName":{"type":"string"},"restartPolicy":{"type":"string"}}},"V1ContainerState":{"type":"object","properties":{"running":{"$ref":"#/components/schemas/V1ContainerStateRunning"},"terminated":{"$ref":"#/components/schemas/V1ContainerStateTerminated"},"waiting":{"$ref":"#/components/schemas/V1ContainerStateWaiting"}}},"V1ContainerStateRunning":{"type":"object","properties":{"startedAt":{"type":"string"}}},"V1ContainerStateTerminated":{"type":"object","properties":{"containerID":{"type":"string"},"exitCode":{"type":"integer"},"finishedAt":{"type":"string"},"message":{"type":"string"},"reason":{"type":"string"},"signal":{"type":"integer"},"startedAt":{"type":"string"}}},"V1ContainerStateWaiting":{"type":"object","properties":{"message":{"type":"string"},"reason":{"type":"string"}}},"V1ContainerStatus":{"type":"object","properties":{"allocatedResources":{"$ref":"#/components/schemas/V1ResourceList"},"allocatedResourcesStatus":{"type":"array","items":{"$ref":"#/components/schemas/V1ResourceStatus"}},"containerID":{"type":"string"},"image":{"type":"string"},"imageID":{"type":"string"},"lastState":{"$ref":"#/components/schemas/V1ContainerState"},"name":{"type":"string"},"ready":{"type":"boolean"},"resources":{"$ref":"#/components/schemas/V1ResourceRequirements"},"restartCount":{"type":"integer"},"started":{"type":"boolean","nullable":true},"state":{"$ref":"#/components/schemas/V1ContainerState"},"stopSignal":{"type":"string","nullable":true},"user":{"$ref":"#/components/schemas/V1ContainerUser"},"volumeMounts":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeMountStatus"}}}},"V1ContainerUser":{"type":"object","properties":{"linux":{"$ref":"#/components/schemas/V1LinuxContainerUser"}}},"V1DownwardAPIProjection":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/V1DownwardAPIVolumeFile"}}}},"V1DownwardAPIVolumeFile":{"type":"object","properties":{"fieldRef":{"$ref":"#/components/schemas/V1ObjectFieldSelector"},"mode":{"type":"integer","nullable":true},"path":{"type":"string"},"resourceFieldRef":{"$ref":"#/components/schemas/V1ResourceFieldSelector"}}},"V1DownwardAPIVolumeSource":{"type":"object","properties":{"defaultMode":{"type":"integer","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/V1DownwardAPIVolumeFile"}}}},"V1EmptyDirVolumeSource":{"type":"object","properties":{"medium":{"type":"string"},"sizeLimit":{"$ref":"#/components/schemas/ResourceQuantity"}}},"V1EnvFromSource":{"type":"object","properties":{"configMapRef":{"$ref":"#/components/schemas/V1ConfigMapEnvSource"},"prefix":{"type":"string"},"secretRef":{"$ref":"#/components/schemas/V1SecretEnvSource"}}},"V1EnvVar":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"},"valueFrom":{"$ref":"#/components/schemas/V1EnvVarSource"}}},"V1EnvVarSource":{"type":"object","properties":{"configMapKeyRef":{"$ref":"#/components/schemas/V1ConfigMapKeySelector"},"fieldRef":{"$ref":"#/components/schemas/V1ObjectFieldSelector"},"resourceFieldRef":{"$ref":"#/components/schemas/V1ResourceFieldSelector"},"secretKeyRef":{"$ref":"#/components/schemas/V1SecretKeySelector"}}},"V1EphemeralContainer":{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"command":{"type":"array","items":{"type":"string"}},"env":{"type":"array","items":{"$ref":"#/components/schemas/V1EnvVar"}},"envFrom":{"type":"array","items":{"$ref":"#/components/schemas/V1EnvFromSource"}},"image":{"type":"string"},"imagePullPolicy":{"type":"string"},"lifecycle":{"$ref":"#/components/schemas/V1Lifecycle"},"livenessProbe":{"$ref":"#/components/schemas/V1Probe"},"name":{"type":"string"},"ports":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerPort"}},"readinessProbe":{"$ref":"#/components/schemas/V1Probe"},"resizePolicy":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerResizePolicy"}},"resources":{"$ref":"#/components/schemas/V1ResourceRequirements"},"restartPolicy":{"type":"string","nullable":true},"securityContext":{"$ref":"#/components/schemas/V1SecurityContext"},"startupProbe":{"$ref":"#/components/schemas/V1Probe"},"stdin":{"type":"boolean"},"stdinOnce":{"type":"boolean"},"targetContainerName":{"type":"string"},"terminationMessagePath":{"type":"string"},"terminationMessagePolicy":{"type":"string"},"tty":{"type":"boolean"},"volumeDevices":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeDevice"}},"volumeMounts":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeMount"}},"workingDir":{"type":"string"}}},"V1EphemeralVolumeSource":{"type":"object","properties":{"volumeClaimTemplate":{"$ref":"#/components/schemas/V1PersistentVolumeClaimTemplate"}}},"V1ExecAction":{"type":"object","properties":{"command":{"type":"array","items":{"type":"string"}}}},"V1FCVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"lun":{"type":"integer","nullable":true},"readOnly":{"type":"boolean"},"targetWWNs":{"type":"array","items":{"type":"string"}},"wwids":{"type":"array","items":{"type":"string"}}}},"V1FieldsV1":{"type":"object"},"V1FlexVolumeSource":{"type":"object","properties":{"driver":{"type":"string"},"fsType":{"type":"string"},"options":{"type":"object","additionalProperties":{"type":"string"}},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"}}},"V1FlockerVolumeSource":{"type":"object","properties":{"datasetName":{"type":"string"},"datasetUUID":{"type":"string"}}},"V1GCEPersistentDiskVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"partition":{"type":"integer"},"pdName":{"type":"string"},"readOnly":{"type":"boolean"}}},"V1GRPCAction":{"type":"object","properties":{"port":{"type":"integer"},"service":{"type":"string","nullable":true}}},"V1GitRepoVolumeSource":{"type":"object","properties":{"directory":{"type":"string"},"repository":{"type":"string"},"revision":{"type":"string"}}},"V1GlusterfsVolumeSource":{"type":"object","properties":{"endpoints":{"type":"string"},"path":{"type":"string"},"readOnly":{"type":"boolean"}}},"V1HTTPGetAction":{"type":"object","properties":{"host":{"type":"string"},"httpHeaders":{"type":"array","items":{"$ref":"#/components/schemas/V1HTTPHeader"}},"path":{"type":"string"},"port":{"$ref":"#/components/schemas/IntstrIntOrString"},"scheme":{"type":"string"}}},"V1HTTPHeader":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"V1HostAlias":{"type":"object","properties":{"hostnames":{"type":"array","items":{"type":"string"}},"ip":{"type":"string"}}},"V1HostIP":{"type":"object","properties":{"ip":{"type":"string"}}},"V1HostPathVolumeSource":{"type":"object","properties":{"path":{"type":"string"},"type":{"type":"string","nullable":true}}},"V1ISCSIVolumeSource":{"type":"object","properties":{"chapAuthDiscovery":{"type":"boolean"},"chapAuthSession":{"type":"boolean"},"fsType":{"type":"string"},"initiatorName":{"type":"string","nullable":true},"iqn":{"type":"string"},"iscsiInterface":{"type":"string"},"lun":{"type":"integer"},"portals":{"type":"array","items":{"type":"string"}},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"targetPortal":{"type":"string"}}},"V1ImageVolumeSource":{"type":"object","properties":{"pullPolicy":{"type":"string"},"reference":{"type":"string"}}},"V1KeyToPath":{"type":"object","properties":{"key":{"type":"string"},"mode":{"type":"integer","nullable":true},"path":{"type":"string"}}},"V1LabelSelector":{"type":"object","properties":{"matchExpressions":{"type":"array","items":{"$ref":"#/components/schemas/V1LabelSelectorRequirement"}},"matchLabels":{"type":"object","additionalProperties":{"type":"string"}}}},"V1LabelSelectorRequirement":{"type":"object","properties":{"key":{"type":"string"},"operator":{"type":"string"},"values":{"type":"array","items":{"type":"string"}}}},"V1Lifecycle":{"type":"object","properties":{"postStart":{"$ref":"#/components/schemas/V1LifecycleHandler"},"preStop":{"$ref":"#/components/schemas/V1LifecycleHandler"},"stopSignal":{"type":"string","nullable":true}}},"V1LifecycleHandler":{"type":"object","properties":{"exec":{"$ref":"#/components/schemas/V1ExecAction"},"httpGet":{"$ref":"#/components/schemas/V1HTTPGetAction"},"sleep":{"$ref":"#/components/schemas/V1SleepAction"},"tcpSocket":{"$ref":"#/components/schemas/V1TCPSocketAction"}}},"V1LinuxContainerUser":{"type":"object","properties":{"gid":{"type":"integer"},"supplementalGroups":{"type":"array","items":{"type":"integer"}},"uid":{"type":"integer"}}},"V1LocalObjectReference":{"type":"object","properties":{"name":{"type":"string"}}},"V1ManagedFieldsEntry":{"type":"object","properties":{"apiVersion":{"type":"string"},"fieldsType":{"type":"string"},"fieldsV1":{"$ref":"#/components/schemas/V1FieldsV1"},"manager":{"type":"string"},"operation":{"type":"string"},"subresource":{"type":"string"},"time":{"type":"string"}}},"V1NFSVolumeSource":{"type":"object","properties":{"path":{"type":"string"},"readOnly":{"type":"boolean"},"server":{"type":"string"}}},"V1NodeAffinity":{"type":"object","properties":{"preferredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1PreferredSchedulingTerm"}},"requiredDuringSchedulingIgnoredDuringExecution":{"$ref":"#/components/schemas/V1NodeSelector"}}},"V1NodeSelector":{"type":"object","properties":{"nodeSelectorTerms":{"type":"array","items":{"$ref":"#/components/schemas/V1NodeSelectorTerm"},"nullable":true}}},"V1NodeSelectorRequirement":{"type":"object","properties":{"key":{"type":"string"},"operator":{"type":"string"},"values":{"type":"array","items":{"type":"string"}}}},"V1NodeSelectorTerm":{"type":"object","properties":{"matchExpressions":{"type":"array","items":{"$ref":"#/components/schemas/V1NodeSelectorRequirement"}},"matchFields":{"type":"array","items":{"$ref":"#/components/schemas/V1NodeSelectorRequirement"}}}},"V1ObjectFieldSelector":{"type":"object","properties":{"apiVersion":{"type":"string"},"fieldPath":{"type":"string"}}},"V1ObjectMeta":{"type":"object","properties":{"annotations":{"type":"object","additionalProperties":{"type":"string"}},"creationTimestamp":{"type":"string"},"deletionGracePeriodSeconds":{"type":"integer","nullable":true},"deletionTimestamp":{"type":"string"},"finalizers":{"type":"array","items":{"type":"string"}},"generateName":{"type":"string"},"generation":{"type":"integer"},"labels":{"type":"object","additionalProperties":{"type":"string"}},"managedFields":{"type":"array","items":{"$ref":"#/components/schemas/V1ManagedFieldsEntry"}},"name":{"type":"string"},"namespace":{"type":"string"},"ownerReferences":{"type":"array","items":{"$ref":"#/components/schemas/V1OwnerReference"}},"resourceVersion":{"type":"string"},"selfLink":{"type":"string"},"uid":{"type":"string"}}},"V1OwnerReference":{"type":"object","properties":{"apiVersion":{"type":"string"},"blockOwnerDeletion":{"type":"boolean","nullable":true},"controller":{"type":"boolean","nullable":true},"kind":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}}},"V1PersistentVolumeClaimSpec":{"type":"object","properties":{"accessModes":{"type":"array","items":{"type":"string"}},"dataSource":{"$ref":"#/components/schemas/V1TypedLocalObjectReference"},"dataSourceRef":{"$ref":"#/components/schemas/V1TypedObjectReference"},"resources":{"$ref":"#/components/schemas/V1VolumeResourceRequirements"},"selector":{"$ref":"#/components/schemas/V1LabelSelector"},"storageClassName":{"type":"string","nullable":true},"volumeAttributesClassName":{"type":"string","nullable":true},"volumeMode":{"type":"string","nullable":true},"volumeName":{"type":"string"}}},"V1PersistentVolumeClaimTemplate":{"type":"object","properties":{"metadata":{"$ref":"#/components/schemas/V1ObjectMeta"},"spec":{"$ref":"#/components/schemas/V1PersistentVolumeClaimSpec"}}},"V1PersistentVolumeClaimVolumeSource":{"type":"object","properties":{"claimName":{"type":"string"},"readOnly":{"type":"boolean"}}},"V1PhotonPersistentDiskVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"pdID":{"type":"string"}}},"V1Pod":{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/components/schemas/V1ObjectMeta"},"spec":{"$ref":"#/components/schemas/V1PodSpec"},"status":{"$ref":"#/components/schemas/V1PodStatus"}}},"V1PodAffinity":{"type":"object","properties":{"preferredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1WeightedPodAffinityTerm"}},"requiredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1PodAffinityTerm"}}}},"V1PodAffinityTerm":{"type":"object","properties":{"labelSelector":{"$ref":"#/components/schemas/V1LabelSelector"},"matchLabelKeys":{"type":"array","items":{"type":"string"}},"mismatchLabelKeys":{"type":"array","items":{"type":"string"}},"namespaceSelector":{"$ref":"#/components/schemas/V1LabelSelector"},"namespaces":{"type":"array","items":{"type":"string"}},"topologyKey":{"type":"string"}}},"V1PodAntiAffinity":{"type":"object","properties":{"preferredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1WeightedPodAffinityTerm"}},"requiredDuringSchedulingIgnoredDuringExecution":{"type":"array","items":{"$ref":"#/components/schemas/V1PodAffinityTerm"}}}},"V1PodCondition":{"type":"object","properties":{"lastProbeTime":{"type":"string"},"lastTransitionTime":{"type":"string"},"message":{"type":"string"},"observedGeneration":{"type":"integer"},"reason":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"}}},"V1PodDNSConfig":{"type":"object","properties":{"nameservers":{"type":"array","items":{"type":"string"}},"options":{"type":"array","items":{"$ref":"#/components/schemas/V1PodDNSConfigOption"}},"searches":{"type":"array","items":{"type":"string"}}}},"V1PodDNSConfigOption":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string","nullable":true}}},"V1PodIP":{"type":"object","properties":{"ip":{"type":"string"}}},"V1PodOS":{"type":"object","properties":{"name":{"type":"string"}}},"V1PodReadinessGate":{"type":"object","properties":{"conditionType":{"type":"string"}}},"V1PodResourceClaim":{"type":"object","properties":{"name":{"type":"string"},"resourceClaimName":{"type":"string","nullable":true},"resourceClaimTemplateName":{"type":"string","nullable":true}}},"V1PodResourceClaimStatus":{"type":"object","properties":{"name":{"type":"string"},"resourceClaimName":{"type":"string","nullable":true}}},"V1PodSchedulingGate":{"type":"object","properties":{"name":{"type":"string"}}},"V1PodSecurityContext":{"type":"object","properties":{"appArmorProfile":{"$ref":"#/components/schemas/V1AppArmorProfile"},"fsGroup":{"type":"integer","nullable":true},"fsGroupChangePolicy":{"type":"string","nullable":true},"runAsGroup":{"type":"integer","nullable":true},"runAsNonRoot":{"type":"boolean","nullable":true},"runAsUser":{"type":"integer","nullable":true},"seLinuxChangePolicy":{"type":"string","nullable":true},"seLinuxOptions":{"$ref":"#/components/schemas/V1SELinuxOptions"},"seccompProfile":{"$ref":"#/components/schemas/V1SeccompProfile"},"supplementalGroups":{"type":"array","items":{"type":"integer"}},"supplementalGroupsPolicy":{"type":"string","nullable":true},"sysctls":{"type":"array","items":{"$ref":"#/components/schemas/V1Sysctl"}},"windowsOptions":{"$ref":"#/components/schemas/V1WindowsSecurityContextOptions"}}},"V1PodSpec":{"type":"object","properties":{"activeDeadlineSeconds":{"type":"integer","nullable":true},"affinity":{"$ref":"#/components/schemas/V1Affinity"},"automountServiceAccountToken":{"type":"boolean","nullable":true},"containers":{"type":"array","items":{"$ref":"#/components/schemas/V1Container"},"nullable":true},"dnsConfig":{"$ref":"#/components/schemas/V1PodDNSConfig"},"dnsPolicy":{"type":"string"},"enableServiceLinks":{"type":"boolean","nullable":true},"ephemeralContainers":{"type":"array","items":{"$ref":"#/components/schemas/V1EphemeralContainer"}},"hostAliases":{"type":"array","items":{"$ref":"#/components/schemas/V1HostAlias"}},"hostIPC":{"type":"boolean"},"hostNetwork":{"type":"boolean"},"hostPID":{"type":"boolean"},"hostUsers":{"type":"boolean","nullable":true},"hostname":{"type":"string"},"imagePullSecrets":{"type":"array","items":{"$ref":"#/components/schemas/V1LocalObjectReference"}},"initContainers":{"type":"array","items":{"$ref":"#/components/schemas/V1Container"}},"nodeName":{"type":"string"},"nodeSelector":{"type":"object","additionalProperties":{"type":"string"}},"os":{"$ref":"#/components/schemas/V1PodOS"},"overhead":{"$ref":"#/components/schemas/V1ResourceList"},"preemptionPolicy":{"type":"string","nullable":true},"priority":{"type":"integer","nullable":true},"priorityClassName":{"type":"string"},"readinessGates":{"type":"array","items":{"$ref":"#/components/schemas/V1PodReadinessGate"}},"resourceClaims":{"type":"array","items":{"$ref":"#/components/schemas/V1PodResourceClaim"}},"resources":{"$ref":"#/components/schemas/V1ResourceRequirements"},"restartPolicy":{"type":"string"},"runtimeClassName":{"type":"string","nullable":true},"schedulerName":{"type":"string"},"schedulingGates":{"type":"array","items":{"$ref":"#/components/schemas/V1PodSchedulingGate"}},"securityContext":{"$ref":"#/components/schemas/V1PodSecurityContext"},"serviceAccount":{"type":"string"},"serviceAccountName":{"type":"string"},"setHostnameAsFQDN":{"type":"boolean","nullable":true},"shareProcessNamespace":{"type":"boolean","nullable":true},"subdomain":{"type":"string"},"terminationGracePeriodSeconds":{"type":"integer","nullable":true},"tolerations":{"type":"array","items":{"$ref":"#/components/schemas/V1Toleration"}},"topologySpreadConstraints":{"type":"array","items":{"$ref":"#/components/schemas/V1TopologySpreadConstraint"}},"volumes":{"type":"array","items":{"$ref":"#/components/schemas/V1Volume"}}}},"V1PodStatus":{"type":"object","properties":{"conditions":{"type":"array","items":{"$ref":"#/components/schemas/V1PodCondition"}},"containerStatuses":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"}},"ephemeralContainerStatuses":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"}},"hostIP":{"type":"string"},"hostIPs":{"type":"array","items":{"$ref":"#/components/schemas/V1HostIP"}},"initContainerStatuses":{"type":"array","items":{"$ref":"#/components/schemas/V1ContainerStatus"}},"message":{"type":"string"},"nominatedNodeName":{"type":"string"},"observedGeneration":{"type":"integer"},"phase":{"type":"string"},"podIP":{"type":"string"},"podIPs":{"type":"array","items":{"$ref":"#/components/schemas/V1PodIP"}},"qosClass":{"type":"string"},"reason":{"type":"string"},"resize":{"type":"string"},"resourceClaimStatuses":{"type":"array","items":{"$ref":"#/components/schemas/V1PodResourceClaimStatus"}},"startTime":{"type":"string"}}},"V1PortworxVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"readOnly":{"type":"boolean"},"volumeID":{"type":"string"}}},"V1PreferredSchedulingTerm":{"type":"object","properties":{"preference":{"$ref":"#/components/schemas/V1NodeSelectorTerm"},"weight":{"type":"integer"}}},"V1Probe":{"type":"object","properties":{"exec":{"$ref":"#/components/schemas/V1ExecAction"},"failureThreshold":{"type":"integer"},"grpc":{"$ref":"#/components/schemas/V1GRPCAction"},"httpGet":{"$ref":"#/components/schemas/V1HTTPGetAction"},"initialDelaySeconds":{"type":"integer"},"periodSeconds":{"type":"integer"},"successThreshold":{"type":"integer"},"tcpSocket":{"$ref":"#/components/schemas/V1TCPSocketAction"},"terminationGracePeriodSeconds":{"type":"integer","nullable":true},"timeoutSeconds":{"type":"integer"}}},"V1ProjectedVolumeSource":{"type":"object","properties":{"defaultMode":{"type":"integer","nullable":true},"sources":{"type":"array","items":{"$ref":"#/components/schemas/V1VolumeProjection"},"nullable":true}}},"V1QuobyteVolumeSource":{"type":"object","properties":{"group":{"type":"string"},"readOnly":{"type":"boolean"},"registry":{"type":"string"},"tenant":{"type":"string"},"user":{"type":"string"},"volume":{"type":"string"}}},"V1RBDVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"image":{"type":"string"},"keyring":{"type":"string"},"monitors":{"type":"array","items":{"type":"string"},"nullable":true},"pool":{"type":"string"},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"user":{"type":"string"}}},"V1ResourceClaim":{"type":"object","properties":{"name":{"type":"string"},"request":{"type":"string"}}},"V1ResourceFieldSelector":{"type":"object","properties":{"containerName":{"type":"string"},"divisor":{"$ref":"#/components/schemas/ResourceQuantity"},"resource":{"type":"string"}}},"V1ResourceHealth":{"type":"object","properties":{"health":{"type":"string"},"resourceID":{"type":"string"}}},"V1ResourceList":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ResourceQuantity"}},"V1ResourceRequirements":{"type":"object","properties":{"claims":{"type":"array","items":{"$ref":"#/components/schemas/V1ResourceClaim"}},"limits":{"$ref":"#/components/schemas/V1ResourceList"},"requests":{"$ref":"#/components/schemas/V1ResourceList"}}},"V1ResourceStatus":{"type":"object","properties":{"name":{"type":"string"},"resources":{"type":"array","items":{"$ref":"#/components/schemas/V1ResourceHealth"}}}},"V1SELinuxOptions":{"type":"object","properties":{"level":{"type":"string"},"role":{"type":"string"},"type":{"type":"string"},"user":{"type":"string"}}},"V1ScaleIOVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"gateway":{"type":"string"},"protectionDomain":{"type":"string"},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"sslEnabled":{"type":"boolean"},"storageMode":{"type":"string"},"storagePool":{"type":"string"},"system":{"type":"string"},"volumeName":{"type":"string"}}},"V1SeccompProfile":{"type":"object","properties":{"localhostProfile":{"type":"string","nullable":true},"type":{"type":"string"}}},"V1Secret":{"type":"object","properties":{"apiVersion":{"type":"string"},"data":{"type":"object","additionalProperties":{"type":"string","format":"base64"}},"immutable":{"type":"boolean","nullable":true},"kind":{"type":"string"},"metadata":{"$ref":"#/components/schemas/V1ObjectMeta"},"stringData":{"type":"object","additionalProperties":{"type":"string"}},"type":{"type":"string"}}},"V1SecretEnvSource":{"type":"object","properties":{"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1SecretKeySelector":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1SecretProjection":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/V1KeyToPath"}},"name":{"type":"string"},"optional":{"type":"boolean","nullable":true}}},"V1SecretVolumeSource":{"type":"object","properties":{"defaultMode":{"type":"integer","nullable":true},"items":{"type":"array","items":{"$ref":"#/components/schemas/V1KeyToPath"}},"optional":{"type":"boolean","nullable":true},"secretName":{"type":"string"}}},"V1SecurityContext":{"type":"object","properties":{"allowPrivilegeEscalation":{"type":"boolean","nullable":true},"appArmorProfile":{"$ref":"#/components/schemas/V1AppArmorProfile"},"capabilities":{"$ref":"#/components/schemas/V1Capabilities"},"privileged":{"type":"boolean","nullable":true},"procMount":{"type":"string","nullable":true},"readOnlyRootFilesystem":{"type":"boolean","nullable":true},"runAsGroup":{"type":"integer","nullable":true},"runAsNonRoot":{"type":"boolean","nullable":true},"runAsUser":{"type":"integer","nullable":true},"seLinuxOptions":{"$ref":"#/components/schemas/V1SELinuxOptions"},"seccompProfile":{"$ref":"#/components/schemas/V1SeccompProfile"},"windowsOptions":{"$ref":"#/components/schemas/V1WindowsSecurityContextOptions"}}},"V1ServiceAccountTokenProjection":{"type":"object","properties":{"audience":{"type":"string"},"expirationSeconds":{"type":"integer","nullable":true},"path":{"type":"string"}}},"V1SleepAction":{"type":"object","properties":{"seconds":{"type":"integer"}}},"V1StorageOSVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"readOnly":{"type":"boolean"},"secretRef":{"$ref":"#/components/schemas/V1LocalObjectReference"},"volumeName":{"type":"string"},"volumeNamespace":{"type":"string"}}},"V1Sysctl":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"V1TCPSocketAction":{"type":"object","properties":{"host":{"type":"string"},"port":{"$ref":"#/components/schemas/IntstrIntOrString"}}},"V1Toleration":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"operator":{"type":"string"},"tolerationSeconds":{"type":"integer","nullable":true},"value":{"type":"string"}}},"V1TopologySpreadConstraint":{"type":"object","properties":{"labelSelector":{"$ref":"#/components/schemas/V1LabelSelector"},"matchLabelKeys":{"type":"array","items":{"type":"string"}},"maxSkew":{"type":"integer"},"minDomains":{"type":"integer","nullable":true},"nodeAffinityPolicy":{"type":"string","nullable":true},"nodeTaintsPolicy":{"type":"string","nullable":true},"topologyKey":{"type":"string"},"whenUnsatisfiable":{"type":"string"}}},"V1TypedLocalObjectReference":{"type":"object","properties":{"apiGroup":{"type":"string","nullable":true},"kind":{"type":"string"},"name":{"type":"string"}}},"V1TypedObjectReference":{"type":"object","properties":{"apiGroup":{"type":"string","nullable":true},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string","nullable":true}}},"V1Volume":{"type":"object","properties":{"awsElasticBlockStore":{"$ref":"#/components/schemas/V1AWSElasticBlockStoreVolumeSource"},"azureDisk":{"$ref":"#/components/schemas/V1AzureDiskVolumeSource"},"azureFile":{"$ref":"#/components/schemas/V1AzureFileVolumeSource"},"cephfs":{"$ref":"#/components/schemas/V1CephFSVolumeSource"},"cinder":{"$ref":"#/components/schemas/V1CinderVolumeSource"},"configMap":{"$ref":"#/components/schemas/V1ConfigMapVolumeSource"},"csi":{"$ref":"#/components/schemas/V1CSIVolumeSource"},"downwardAPI":{"$ref":"#/components/schemas/V1DownwardAPIVolumeSource"},"emptyDir":{"$ref":"#/components/schemas/V1EmptyDirVolumeSource"},"ephemeral":{"$ref":"#/components/schemas/V1EphemeralVolumeSource"},"fc":{"$ref":"#/components/schemas/V1FCVolumeSource"},"flexVolume":{"$ref":"#/components/schemas/V1FlexVolumeSource"},"flocker":{"$ref":"#/components/schemas/V1FlockerVolumeSource"},"gcePersistentDisk":{"$ref":"#/components/schemas/V1GCEPersistentDiskVolumeSource"},"gitRepo":{"$ref":"#/components/schemas/V1GitRepoVolumeSource"},"glusterfs":{"$ref":"#/components/schemas/V1GlusterfsVolumeSource"},"hostPath":{"$ref":"#/components/schemas/V1HostPathVolumeSource"},"image":{"$ref":"#/components/schemas/V1ImageVolumeSource"},"iscsi":{"$ref":"#/components/schemas/V1ISCSIVolumeSource"},"name":{"type":"string"},"nfs":{"$ref":"#/components/schemas/V1NFSVolumeSource"},"persistentVolumeClaim":{"$ref":"#/components/schemas/V1PersistentVolumeClaimVolumeSource"},"photonPersistentDisk":{"$ref":"#/components/schemas/V1PhotonPersistentDiskVolumeSource"},"portworxVolume":{"$ref":"#/components/schemas/V1PortworxVolumeSource"},"projected":{"$ref":"#/components/schemas/V1ProjectedVolumeSource"},"quobyte":{"$ref":"#/components/schemas/V1QuobyteVolumeSource"},"rbd":{"$ref":"#/components/schemas/V1RBDVolumeSource"},"scaleIO":{"$ref":"#/components/schemas/V1ScaleIOVolumeSource"},"secret":{"$ref":"#/components/schemas/V1SecretVolumeSource"},"storageos":{"$ref":"#/components/schemas/V1StorageOSVolumeSource"},"vsphereVolume":{"$ref":"#/components/schemas/V1VsphereVirtualDiskVolumeSource"}}},"V1VolumeDevice":{"type":"object","properties":{"devicePath":{"type":"string"},"name":{"type":"string"}}},"V1VolumeMount":{"type":"object","properties":{"mountPath":{"type":"string"},"mountPropagation":{"type":"string","nullable":true},"name":{"type":"string"},"readOnly":{"type":"boolean"},"recursiveReadOnly":{"type":"string","nullable":true},"subPath":{"type":"string"},"subPathExpr":{"type":"string"}}},"V1VolumeMountStatus":{"type":"object","properties":{"mountPath":{"type":"string"},"name":{"type":"string"},"readOnly":{"type":"boolean"},"recursiveReadOnly":{"type":"string","nullable":true}}},"V1VolumeProjection":{"type":"object","properties":{"clusterTrustBundle":{"$ref":"#/components/schemas/V1ClusterTrustBundleProjection"},"configMap":{"$ref":"#/components/schemas/V1ConfigMapProjection"},"downwardAPI":{"$ref":"#/components/schemas/V1DownwardAPIProjection"},"secret":{"$ref":"#/components/schemas/V1SecretProjection"},"serviceAccountToken":{"$ref":"#/components/schemas/V1ServiceAccountTokenProjection"}}},"V1VolumeResourceRequirements":{"type":"object","properties":{"limits":{"$ref":"#/components/schemas/V1ResourceList"},"requests":{"$ref":"#/components/schemas/V1ResourceList"}}},"V1VsphereVirtualDiskVolumeSource":{"type":"object","properties":{"fsType":{"type":"string"},"storagePolicyID":{"type":"string"},"storagePolicyName":{"type":"string"},"volumePath":{"type":"string"}}},"V1WeightedPodAffinityTerm":{"type":"object","properties":{"podAffinityTerm":{"$ref":"#/components/schemas/V1PodAffinityTerm"},"weight":{"type":"integer"}}},"V1WindowsSecurityContextOptions":{"type":"object","properties":{"gmsaCredentialSpec":{"type":"string","nullable":true},"gmsaCredentialSpecName":{"type":"string","nullable":true},"hostProcess":{"type":"boolean","nullable":true},"runAsUserName":{"type":"string","nullable":true}}}}}} \ No newline at end of file diff --git a/docs/openapi/plugin-openapi.json b/docs/openapi/plugin-openapi.json index 825ec318..761b9510 100644 --- a/docs/openapi/plugin-openapi.json +++ b/docs/openapi/plugin-openapi.json @@ -44,6 +44,48 @@ } } }, + "/createPodGroup": { + "post": { + "summary": "Create Pod Group", + "operationId": "create_pod_group_createPodGroup_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RetrievedPodGroupData" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/CreateStruct" + }, + "type": "array", + "title": "Response Create Pod Group Createpodgroup Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, "/delete": { "post": { "summary": "Delete Pod", @@ -984,6 +1026,27 @@ ], "title": "PodVolume" }, + "RetrievedPodGroupData": { + "properties": { + "groupUID": { + "type": "string", + "title": "Group Uid" + }, + "pods": { + "items": { + "$ref": "#/components/schemas/Pod" + }, + "type": "array", + "title": "Pods" + } + }, + "type": "object", + "required": [ + "groupUID", + "pods" + ], + "title": "RetrievedPodGroupData" + }, "Secret": { "properties": { "metadata": { diff --git a/pkg/interlink/api/create.go b/pkg/interlink/api/create.go index e5d38a7e..6800a86d 100644 --- a/pkg/interlink/api/create.go +++ b/pkg/interlink/api/create.go @@ -148,18 +148,7 @@ func (h *InterLinkHandler) CreateHandler(w http.ResponseWriter, r *http.Request) log.G(h.Ctx).Infof("Updated JobScript successfully (len=%d)", len(data.JobScript)) case h.Config.JobScriptTemplate != "": - - tmp, err := template.ParseFiles(h.Config.JobScriptTemplate) - if err != nil { - statusCode = http.StatusInternalServerError - log.G(h.Ctx).Error(err) - w.WriteHeader(statusCode) - types.SetSpanError(span, statusCode, err) - return - } - - var tpl bytes.Buffer - err = tmp.Execute(&tpl, data) + data.JobScript, err = renderJobScript(h.Config.JobScriptTemplate, data) if err != nil { statusCode = http.StatusInternalServerError log.G(h.Ctx).Error(err) @@ -167,8 +156,6 @@ func (h *InterLinkHandler) CreateHandler(w http.ResponseWriter, r *http.Request) types.SetSpanError(span, statusCode, err) return } - - data.JobScript = tpl.String() } // updated to handle single data @@ -213,3 +200,17 @@ func (h *InterLinkHandler) CreateHandler(w http.ResponseWriter, r *http.Request) // is set here. types.SetSpanOK(span, 0) } + +// renderJobScript fills the job script template at path with the data of one pod. +func renderJobScript(path string, data types.RetrievedPodData) (string, error) { + tmp, err := template.ParseFiles(path) + if err != nil { + return "", err + } + + var tpl bytes.Buffer + if err := tmp.Execute(&tpl, data); err != nil { + return "", err + } + return tpl.String(), nil +} diff --git a/pkg/interlink/api/createpodgroup.go b/pkg/interlink/api/createpodgroup.go new file mode 100644 index 00000000..55e6c201 --- /dev/null +++ b/pkg/interlink/api/createpodgroup.go @@ -0,0 +1,228 @@ +package api + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "html" + "io" + "net/http" + "time" + + "github.com/containerd/containerd/log" + + types "github.com/interlink-hq/interlink/pkg/interlink" + + "go.opentelemetry.io/otel/attribute" + trace "go.opentelemetry.io/otel/trace" +) + +// CreatePodGroupHandler handles HTTP POST requests to create the pods of one pod group in +// a single call, so that the sidecar plugin can run the whole group as one job. +// Every pod is prepared as CreateHandler prepares it (ConfigMaps, Secrets and projected +// volumes split by container, optional job script), and the group is forwarded to the +// plugin's /createPodGroup. It is meant for plugins that list "pod-group-create" in their +// ping answer; status, logs and delete stay per pod. +// +// Request body: JSON-encoded PodGroupCreateRequests +// Response: JSON-encoded CreateStruct array, one entry per pod in the order of the +// request, all with the job ID of the group +// +// HTTP Status Codes: +// - 200: Group creation request processed successfully +// - 400: Bad request (malformed body, no pods, or a permanent refusal of the plugin) +// - 503: The plugin cannot take the group now +// - 500: Internal server error (job script or sidecar communication failures) +func (h *InterLinkHandler) CreatePodGroupHandler(w http.ResponseWriter, r *http.Request) { + start := time.Now().UnixMicro() + // One session for the whole group: GetSessionContext, which startAPITrace calls, + // makes up a new one on every call when the request has none. + ctx, span, sessionContext := h.startAPITrace(r, "CreatePodGroupAPI", "/createPodGroup", start) + defer span.End() + defer types.SetDurationSpan(start, span) + // X-Forwarded-Email and X-Forwarded-User identify a person, so they follow + // the same opt-in rule as the other client details. + if h.Config.Tracing.Detailed { + defer types.SetInfoFromHeaders(span, &r.Header) + } + + log.G(h.Ctx).Info("InterLink: received CreatePodGroup call") + + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.G(h.Ctx).Error(err) + types.SetSpanError(span, http.StatusInternalServerError, err) + return + } + setRequestBodySize(span, bodyBytes) + + var request types.PodGroupCreateRequests + if err := json.Unmarshal(bodyBytes, &request); err != nil { + writePodGroupError(h.Ctx, w, span, http.StatusBadRequest, fmt.Errorf("invalid request body: %w", err)) + return + } + if len(request.Pods) == 0 { + writePodGroupError(h.Ctx, w, span, http.StatusBadRequest, fmt.Errorf("pod group %s has no pods", request.GroupUID)) + return + } + + span.SetAttributes( + attribute.String("group.uid", request.GroupUID), + attribute.Int("group.pods", len(request.Pods)), + ) + + retrievedData := types.RetrievedPodGroupData{ + GroupUID: request.GroupUID, + Pods: make([]types.RetrievedPodData, 0, len(request.Pods)), + } + for _, pod := range request.Pods { + log.G(h.Ctx).Infof("InterLink: preparing pod %s/%s of pod group %s", pod.Pod.Namespace, pod.Pod.Name, request.GroupUID) + + data, err := getData(ctx, h.Config, pod, span) + if err != nil { + writePodGroupError(h.Ctx, w, span, http.StatusInternalServerError, fmt.Errorf("pod %s/%s: %w", pod.Pod.Namespace, pod.Pod.Name, err)) + return + } + + statusCode, err := h.podGroupJobScript(ctx, pod, &data, start, span, sessionContext) + if err != nil { + writePodGroupError(h.Ctx, w, span, statusCode, fmt.Errorf("pod %s/%s: job script: %w", pod.Pod.Namespace, pod.Pod.Name, err)) + return + } + + retrievedData.Pods = append(retrievedData.Pods, data) + } + + bodyBytes, err = json.Marshal(retrievedData) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.G(h.Ctx).Error(err) + types.SetSpanError(span, http.StatusInternalServerError, err) + return + } + log.G(h.Ctx).Debug(string(bodyBytes)) + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, h.SidecarEndpoint+"/createPodGroup", bytes.NewReader(bodyBytes)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.G(h.Ctx).Error(err) + types.SetSpanError(span, http.StatusInternalServerError, err) + return + } + + log.G(h.Ctx).Infof("InterLink: forwarding CreatePodGroup call to sidecar (group %s, %d pods)", request.GroupUID, len(request.Pods)) + + ForwardAccessToken(r, req) + _, err = ReqWithError(ctx, req, w, start, span, true, false, sessionContext, h.ClientHTTP) + if err != nil { + // ReqWithError has already marked the span as failed. + log.L.Error(err) + return + } + + // The return code was already recorded by ReqWithError, so only the outcome + // is set here. + types.SetSpanOK(span, 0) +} + +// podGroupJobScript fills the job script of one pod of a group as CreateHandler does, from +// the JobScriptBuilderURL of the pod or from the configured JobScriptTemplate. Unlike +// CreateHandler it does not answer the caller: the group gets one answer, so it returns +// the status code to answer with when it fails. +func (h *InterLinkHandler) podGroupJobScript( + ctx context.Context, + pod types.PodCreateRequests, + data *types.RetrievedPodData, + start int64, + span trace.Span, + sessionContext string, +) (int, error) { + switch { + case pod.JobScriptBuilderURL != "": + log.G(h.Ctx).Info("JobScriptBuilderURL: ", pod.JobScriptBuilderURL) + if h.Config.JobScriptBuildConfig == nil { + return http.StatusInternalServerError, errors.New("JobScript URL requested, but interlink does not have any Script build config set") + } + + data.JobScriptBuild = *h.Config.JobScriptBuildConfig + + bodyBytes, err := json.Marshal(data) + if err != nil { + return http.StatusInternalServerError, err + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, pod.JobScriptBuilderURL, bytes.NewReader(bodyBytes)) + if err != nil { + return http.StatusInternalServerError, err + } + req.Header.Set("Content-Type", "application/json") + + // No ForwardAccessToken here, as in CreateHandler: JobScriptBuilderURL comes from + // the pod spec and can point outside the deployment. + // ReqWithError writes the builder's status code to its ResponseWriter, which must + // not be the caller's: the caller is answered once for the whole group. + var builderStatus statusRecorder + script, err := ReqWithError(ctx, req, &builderStatus, start, span, false, true, sessionContext, http.DefaultClient) + if err != nil { + statusCode := builderStatus.code + if statusCode == 0 || statusCode == http.StatusOK { + statusCode = http.StatusInternalServerError + } + return statusCode, err + } + + data.JobScript = string(script) + log.G(h.Ctx).Infof("Updated JobScript of pod %s successfully (len=%d)", data.Pod.Name, len(data.JobScript)) + + case h.Config.JobScriptTemplate != "": + script, err := renderJobScript(h.Config.JobScriptTemplate, *data) + if err != nil { + return http.StatusInternalServerError, err + } + data.JobScript = script + } + return http.StatusOK, nil +} + +// writePodGroupError answers the caller with statusCode and the error text, escaped as +// ReqWithError escapes the errors of the sidecar, and marks the span as failed with them. +func writePodGroupError(ctx context.Context, w http.ResponseWriter, span trace.Span, statusCode int, err error) { + log.G(ctx).Error(err) + w.WriteHeader(statusCode) + types.SetSpanError(span, statusCode, err) + if _, werr := w.Write([]byte(html.EscapeString(err.Error()))); werr != nil { + log.G(ctx).Error(werr) + } +} + +// statusRecorder is a ResponseWriter that keeps only the status code written to it, for +// calls whose answer must not reach the caller directly. +type statusRecorder struct { + header http.Header + code int +} + +func (s *statusRecorder) Header() http.Header { + if s.header == nil { + s.header = http.Header{} + } + return s.header +} + +// WriteHeader keeps the first status code only, as net/http does. +func (s *statusRecorder) WriteHeader(statusCode int) { + if s.code == 0 { + s.code = statusCode + } +} + +func (s *statusRecorder) Write(p []byte) (int, error) { + s.WriteHeader(http.StatusOK) + return len(p), nil +} + +// Flush makes statusRecorder an http.Flusher, which ReqWithError otherwise warns about. +func (s *statusRecorder) Flush() {} diff --git a/pkg/interlink/api/createpodgroup_test.go b/pkg/interlink/api/createpodgroup_test.go new file mode 100644 index 00000000..1fd3a5c0 --- /dev/null +++ b/pkg/interlink/api/createpodgroup_test.go @@ -0,0 +1,408 @@ +package api + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/codes" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8stypes "k8s.io/apimachinery/pkg/types" + + types "github.com/interlink-hq/interlink/pkg/interlink" +) + +const ( + testGroupUID = "0b7c6f6e-5c1d-4d6a-9f3e-2a1b0c9d8e7f" + testPodGroupSession = "Request-podgroup-123" + testMainContainer = "main" + testSidecarContainer = "sidecar" + testConfigMapName = "settings" + testSecretName = "creds" + testProjectedVolume = "token" + testCreatePodGroupPath = "/createPodGroup" +) + +// sidecarCall is what the fake sidecar received in one call. +type sidecarCall struct { + path string + session string + token string + body types.RetrievedPodGroupData +} + +// newPodGroupSidecar starts a fake sidecar that records every call and answers each one +// with statusCode and answer, and returns an InterLinkHandler pointed at it. +func newPodGroupSidecar(t *testing.T, statusCode int, answer string) (*InterLinkHandler, chan sidecarCall) { + t.Helper() + calls := make(chan sidecarCall, 4) + sidecar := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + call := sidecarCall{path: r.URL.Path, session: r.Header.Get("InterLink-Http-Session"), token: r.Header.Get(accessTokenHeader)} + assert.NoError(t, json.NewDecoder(r.Body).Decode(&call.body)) + calls <- call + + w.WriteHeader(statusCode) + if _, err := io.WriteString(w, answer); err != nil { + panic(err) + } + })) + t.Cleanup(sidecar.Close) + + handler := &InterLinkHandler{ + Ctx: context.Background(), + SidecarEndpoint: sidecar.URL, + ClientHTTP: sidecar.Client(), + } + return handler, calls +} + +// podGroupPod returns a pod of the test group with two containers: the main one mounts +// the ConfigMap and the projected volume, the sidecar one mounts the Secret only. The +// projected volume holds the pod name, as downward API files do. +func podGroupPod(name string) types.PodCreateRequests { + return types.PodCreateRequests{ + Pod: v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testDefaultNamespace, + UID: k8stypes.UID("uid-" + name), + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + {Name: testMainContainer, VolumeMounts: []v1.VolumeMount{{Name: "config"}, {Name: testProjectedVolume}}}, + {Name: testSidecarContainer, VolumeMounts: []v1.VolumeMount{{Name: "secret"}}}, + }, + Volumes: []v1.Volume{ + {Name: "config", VolumeSource: v1.VolumeSource{ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{Name: testConfigMapName}, + }}}, + {Name: "secret", VolumeSource: v1.VolumeSource{Secret: &v1.SecretVolumeSource{SecretName: testSecretName}}}, + {Name: testProjectedVolume, VolumeSource: v1.VolumeSource{Projected: &v1.ProjectedVolumeSource{}}}, + }, + }, + }, + ConfigMaps: []v1.ConfigMap{{ObjectMeta: metav1.ObjectMeta{Name: testConfigMapName}}}, + Secrets: []v1.Secret{{ObjectMeta: metav1.ObjectMeta{Name: testSecretName}}}, + ProjectedVolumeMaps: []v1.ConfigMap{{ + ObjectMeta: metav1.ObjectMeta{Name: testProjectedVolume}, + Data: map[string]string{"pod-name": name}, + }}, + } +} + +// podGroupRequest returns a POST /createPodGroup of the test group: a launcher pod and two +// workers, in rank order. +func podGroupRequest(t *testing.T, mutate func(*types.PodCreateRequests)) *http.Request { + t.Helper() + request := types.PodGroupCreateRequests{ + GroupUID: testGroupUID, + Pods: []types.PodCreateRequests{podGroupPod("launcher-0"), podGroupPod("worker-0"), podGroupPod("worker-1")}, + } + if mutate != nil { + for i := range request.Pods { + mutate(&request.Pods[i]) + } + } + body, err := json.Marshal(request) + require.NoError(t, err) + + r := httptest.NewRequest(http.MethodPost, testCreatePodGroupPath, bytes.NewReader(body)) + r.Header.Set("InterLink-Http-Session", testPodGroupSession) + return r +} + +func TestCreatePodGroupHandler_ForwardsGroup(t *testing.T) { + _, cleanup := setupTestTracer() + defer cleanup() + + answer := `[{"PodUID":"uid-launcher-0","PodJID":"42"},{"PodUID":"uid-worker-0","PodJID":"42"},{"PodUID":"uid-worker-1","PodJID":"42"}]` + handler, calls := newPodGroupSidecar(t, http.StatusOK, answer) + + w := httptest.NewRecorder() + handler.CreatePodGroupHandler(w, podGroupRequest(t, nil)) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, answer, w.Body.String()) + + require.Len(t, calls, 1) + call := <-calls + assert.Equal(t, testCreatePodGroupPath, call.path) + assert.Equal(t, testPodGroupSession, call.session) + assert.Equal(t, testGroupUID, call.body.GroupUID) + + pods := call.body.Pods + require.Len(t, pods, 3) + for i, name := range []string{"launcher-0", "worker-0", "worker-1"} { + pod := pods[i] + assert.Equal(t, name, pod.Pod.Name) + require.Len(t, pod.Containers, 2) + + mainData, sidecarData := pod.Containers[0], pod.Containers[1] + assert.Equal(t, testMainContainer, mainData.Name) + require.Len(t, mainData.ConfigMaps, 1) + assert.Equal(t, testConfigMapName, mainData.ConfigMaps[0].Name) + assert.Empty(t, mainData.Secrets) + require.Len(t, mainData.ProjectedVolumeMaps, 1) + assert.Equal(t, name, mainData.ProjectedVolumeMaps[0].Data["pod-name"], "each pod keeps its own projected volume") + + assert.Equal(t, testSidecarContainer, sidecarData.Name) + require.Len(t, sidecarData.Secrets, 1) + assert.Equal(t, testSecretName, sidecarData.Secrets[0].Name) + assert.Empty(t, sidecarData.ConfigMaps) + assert.Empty(t, sidecarData.ProjectedVolumeMaps) + } +} + +func TestCreatePodGroupHandler_RelaysSidecarErrors(t *testing.T) { + _, cleanup := setupTestTracer() + defer cleanup() + + tests := []struct { + name string + statusCode int + answer string + }{ + { + name: "transient refusal", + statusCode: http.StatusServiceUnavailable, + answer: "group still runs as job 42", + }, + { + name: "permanent refusal", + statusCode: http.StatusBadRequest, + answer: "this plugin does not run pod groups", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + handler, calls := newPodGroupSidecar(t, tt.statusCode, tt.answer) + + w := httptest.NewRecorder() + handler.CreatePodGroupHandler(w, podGroupRequest(t, nil)) + + assert.Equal(t, tt.statusCode, w.Code) + assert.Contains(t, w.Body.String(), tt.answer) + assert.Len(t, calls, 1) + }) + } +} + +// The group call records its outcome on the span as the other handlers do, and the +// caller's identity only with detailed tracing. +func TestCreatePodGroupHandler_Span(t *testing.T) { + const okAnswer = `[{"PodUID":"uid-launcher-0","PodJID":"42"},{"PodUID":"uid-worker-0","PodJID":"42"},{"PodUID":"uid-worker-1","PodJID":"42"}]` + withCaller := func(r *http.Request) *http.Request { + r.Header.Set("X-Forwarded-Email", "someone@example.org") + return r + } + + t.Run("ok when the sidecar takes the group", func(t *testing.T) { + exporter := newSpanRecorder(t) + handler, _ := newPodGroupSidecar(t, http.StatusOK, okAnswer) + + handler.CreatePodGroupHandler(httptest.NewRecorder(), withCaller(podGroupRequest(t, nil))) + + span := exportedSpan(t, exporter, "CreatePodGroupAPI") + assert.Equal(t, codes.Ok, span.Status.Code) + _, ok := spanAttrString(span, "X-Forwarded-Email") + assert.False(t, ok, "the caller email must not be on the span unless tracing is detailed") + }) + + t.Run("error when the sidecar refuses it", func(t *testing.T) { + exporter := newSpanRecorder(t) + handler, _ := newPodGroupSidecar(t, http.StatusServiceUnavailable, "busy") + + handler.CreatePodGroupHandler(httptest.NewRecorder(), podGroupRequest(t, nil)) + + span := exportedSpan(t, exporter, "CreatePodGroupAPI") + assert.Equal(t, codes.Error, span.Status.Code) + code, ok := spanAttrInt(span, "exit.code") + assert.True(t, ok) + assert.Equal(t, int64(http.StatusServiceUnavailable), code) + }) + + t.Run("error when the request has no pods", func(t *testing.T) { + exporter := newSpanRecorder(t) + handler, calls := newPodGroupSidecar(t, http.StatusOK, okAnswer) + body, err := json.Marshal(types.PodGroupCreateRequests{GroupUID: testGroupUID}) + require.NoError(t, err) + + handler.CreatePodGroupHandler(httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, testCreatePodGroupPath, bytes.NewReader(body))) + + span := exportedSpan(t, exporter, "CreatePodGroupAPI") + assert.Equal(t, codes.Error, span.Status.Code) + code, ok := spanAttrInt(span, "exit.code") + assert.True(t, ok) + assert.Equal(t, int64(http.StatusBadRequest), code) + assert.Empty(t, calls) + }) + + t.Run("the caller is recorded with detailed tracing", func(t *testing.T) { + exporter := newSpanRecorder(t) + handler, _ := newPodGroupSidecar(t, http.StatusOK, okAnswer) + handler.Config.Tracing.Detailed = true + + handler.CreatePodGroupHandler(httptest.NewRecorder(), withCaller(podGroupRequest(t, nil))) + + email, ok := spanAttrString(exportedSpan(t, exporter, "CreatePodGroupAPI"), "X-Forwarded-Email") + require.True(t, ok) + assert.Equal(t, "someone@example.org", email) + }) +} + +func TestCreatePodGroupHandler_RefusesBadRequests(t *testing.T) { + _, cleanup := setupTestTracer() + defer cleanup() + + tests := []struct { + name string + body string + }{ + { + name: "malformed body", + body: `{"groupUID":`, + }, + { + name: "no pods", + body: `{"groupUID":"` + testGroupUID + `"}`, + }, + { + name: "empty pods", + body: `{"groupUID":"` + testGroupUID + `","pods":[]}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + handler, calls := newPodGroupSidecar(t, http.StatusOK, "[]") + + r := httptest.NewRequest(http.MethodPost, testCreatePodGroupPath, bytes.NewBufferString(tt.body)) + w := httptest.NewRecorder() + handler.CreatePodGroupHandler(w, r) + + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.NotEmpty(t, w.Body.String()) + assert.Empty(t, calls, "the sidecar must not be called") + }) + } +} + +func TestCreatePodGroupHandler_JobScriptTemplate(t *testing.T) { + _, cleanup := setupTestTracer() + defer cleanup() + + templatePath := filepath.Join(t.TempDir(), "job.sh.tmpl") + require.NoError(t, os.WriteFile(templatePath, []byte("run {{.Pod.Name}}"), 0o600)) + + handler, calls := newPodGroupSidecar(t, http.StatusOK, "[]") + handler.Config.JobScriptTemplate = templatePath + + w := httptest.NewRecorder() + handler.CreatePodGroupHandler(w, podGroupRequest(t, nil)) + + assert.Equal(t, http.StatusOK, w.Code) + require.Len(t, calls, 1) + for _, pod := range (<-calls).body.Pods { + assert.Equal(t, "run "+pod.Pod.Name, pod.JobScript) + } +} + +func TestCreatePodGroupHandler_JobScriptBuilder(t *testing.T) { + _, cleanup := setupTestTracer() + defer cleanup() + + tests := []struct { + name string + builderStatus int + wantStatus int + }{ + { + name: "builder answers", + builderStatus: http.StatusOK, + wantStatus: http.StatusOK, + }, + { + name: "builder unavailable", + builderStatus: http.StatusServiceUnavailable, + wantStatus: http.StatusServiceUnavailable, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + builder := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var data types.RetrievedPodData + assert.NoError(t, json.NewDecoder(r.Body).Decode(&data)) + assert.Equal(t, testPodGroupSession, r.Header.Get("InterLink-Http-Session")) + + w.WriteHeader(tt.builderStatus) + if _, err := io.WriteString(w, "built for "+data.Pod.Name); err != nil { + panic(err) + } + })) + defer builder.Close() + + handler, calls := newPodGroupSidecar(t, http.StatusOK, "[]") + handler.Config.JobScriptBuildConfig = &types.ScriptBuildConfig{} + + w := httptest.NewRecorder() + handler.CreatePodGroupHandler(w, podGroupRequest(t, func(pod *types.PodCreateRequests) { + pod.JobScriptBuilderURL = builder.URL + })) + + assert.Equal(t, tt.wantStatus, w.Code) + if tt.wantStatus != http.StatusOK { + assert.Contains(t, w.Body.String(), "built for launcher-0") + assert.Empty(t, calls, "the sidecar must not be called") + return + } + require.Len(t, calls, 1) + for _, pod := range (<-calls).body.Pods { + assert.Equal(t, "built for "+pod.Pod.Name, pod.JobScript) + } + }) + } +} + +// The caller's access token reaches the sidecar, as on /create, and never the job script +// builder, whose URL comes from the pod spec. +func TestCreatePodGroupHandler_ForwardsAccessTokenToSidecarOnly(t *testing.T) { + _, cleanup := setupTestTracer() + defer cleanup() + + var builderTokens tokenLog + builder := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + builderTokens.record(r) + if _, err := io.WriteString(w, "script"); err != nil { + panic(err) + } + })) + defer builder.Close() + + handler, calls := newPodGroupSidecar(t, http.StatusOK, "[]") + handler.Config.JobScriptBuildConfig = &types.ScriptBuildConfig{} + + r := podGroupRequest(t, func(pod *types.PodCreateRequests) { + pod.JobScriptBuilderURL = builder.URL + }) + r.Header.Set(accessTokenHeader, testAccessToken) + w := httptest.NewRecorder() + handler.CreatePodGroupHandler(w, r) + + assert.Equal(t, http.StatusOK, w.Code) + require.Len(t, calls, 1) + assert.Equal(t, testAccessToken, (<-calls).token) + token, called := builderTokens.at("/") + require.True(t, called) + assert.Empty(t, token, "the builder must not get the caller's token") +} diff --git a/pkg/interlink/types.go b/pkg/interlink/types.go index bbf9bfd9..1a20bd20 100644 --- a/pkg/interlink/types.go +++ b/pkg/interlink/types.go @@ -101,6 +101,27 @@ type RetrievedPodData struct { JobScript string `json:"jobScript,omitempty"` } +// PodGroupCreateRequests represents a request to create the pods of one pod group in a +// single call, so that the plugin can run them as one job. The order of the pods gives their +// ranks, and rank 0 is the head. Each pod carries its own ConfigMaps, Secrets and projected +// volumes, because projected and downward API files differ from pod to pod. +type PodGroupCreateRequests struct { + // GroupUID identifies the pod group, for instance the UID of the workload it runs + GroupUID string `json:"groupUID"` + // Pods contains the pods of the group in rank order, each as it is sent to /create + Pods []PodCreateRequests `json:"pods"` +} + +// RetrievedPodGroupData represents the pods of one pod group with all their associated data. +// It is what interLink sends to the plugin's /createPodGroup: each pod is prepared as for +// /create, in the order of the PodGroupCreateRequests. +type RetrievedPodGroupData struct { + // GroupUID identifies the pod group, as in PodGroupCreateRequests + GroupUID string `json:"groupUID"` + // Pods contains the pods of the group in rank order, each as it is sent to the plugin's /create + Pods []RetrievedPodData `json:"pods"` +} + // ContainerLogOpts specifies options for retrieving container logs from sidecar plugins. // These options control how logs are fetched, filtered, and formatted, matching // Kubernetes' standard log retrieval parameters.