this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Added Loom spindle.

Kept homebrew k8s-based workflow runner just in case

+1756 -99
-46
.tangled/workflows/notify.yml
··· 1 - when: 2 - - event: ["push"] 3 - branch: ["main"] 4 - 5 - engine: "kubernetes" 6 - 7 - job: 8 - apiVersion: batch/v1 9 - kind: Job 10 - spec: 11 - backoffLimit: 0 12 - template: 13 - spec: 14 - restartPolicy: Never 15 - volumes: 16 - - name: secrets 17 - secret: 18 - secretName: telegram-credentials 19 - containers: 20 - - name: notify 21 - image: busybox:1 22 - volumeMounts: 23 - - name: secrets 24 - mountPath: /secrets 25 - readOnly: true 26 - command: ["sh", "-c"] 27 - args: 28 - - | 29 - BOT_TOKEN=$(tr -d '[:space:]' < /secrets/bot-token) 30 - CHAT_ID=$(tr -d '[:space:]' < /secrets/chat-id) 31 - 32 - [ "$CI_REF_TYPE" = "tag" ] \ 33 - && ref_label="Tag: \`$CI_REF_NAME\`" \ 34 - || ref_label="Branch: \`$CI_REF_NAME\`" 35 - 36 - text="*Push to $CI_REPO_NAME* 37 - $ref_label 38 - 39 - \`$CI_SHORT_SHA\`" 40 - 41 - json_text=$(printf '%s' "$text" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk 'NR>1{printf "\\n"}{printf "%s",$0}') 42 - 43 - wget -q -O /dev/null \ 44 - --header='Content-Type: application/json' \ 45 - --post-data="{\"chat_id\":\"$CHAT_ID\",\"text\":\"$json_text\",\"parse_mode\":\"Markdown\"}" \ 46 - "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage"
+16
.tangled/workflows/test.yml
··· 1 + when: 2 + - event: ["push"] 3 + tag: ["v*"] 4 + branch : ["*"] 5 + 6 + engine: kubernetes 7 + image: golang:1.25-trixie 8 + architecture: amd64 9 + 10 + environment: 11 + IMAGE_REGISTRY: atcr.io 12 + 13 + steps: 14 + - name: test environment vars 15 + command: | 16 + printenv
-51
.tangled/workflows/validate.yml
··· 1 - when: 2 - - event: ["push"] 3 - branch: ["main"] 4 - - event: ["pull_request"] 5 - branch: ["main"] 6 - 7 - engine: "nixery" 8 - 9 - dependencies: 10 - nixpkgs: 11 - - kustomize 12 - - kubeconform 13 - - opentofu 14 - - yamllint 15 - - shellcheck 16 - 17 - steps: 18 - - name: "Stub encrypted secrets" 19 - command: | 20 - for f in k8s/pds/admin-password.secret \ 21 - k8s/pds/jwt.secret \ 22 - k8s/pds/plc-rotation-key.secret \ 23 - k8s/pds/smtp-url.secret \ 24 - k8s/shared/s3-access-key.secret \ 25 - k8s/shared/s3-secret-key.secret; do 26 - echo "stub" > "$f" 27 - done 28 - 29 - mkdir -p keypair 30 - echo "stub" > keypair/id_ed25519_homelab 31 - echo "stub" > keypair/id_ed25519_homelab.pub 32 - 33 - - name: "Lint YAML" 34 - command: | 35 - yamllint -d '{extends: relaxed, rules: {line-length: disable}}' \ 36 - k8s/**/*.yaml 37 - 38 - - name: "Lint shell scripts" 39 - command: shellcheck k8s/shared/backup.sh 40 - 41 - - name: "Validate kustomize manifests" 42 - command: | 43 - kustomize build k8s/ | kubeconform \ 44 - -strict \ 45 - -ignore-missing-schemas \ 46 - -summary 47 - 48 - - name: "Validate OpenTofu" 49 - command: | 50 - tofu init -backend=false 51 - tofu validate
+12
k8s/ci/cert.yaml
··· 1 + apiVersion: cert-manager.io/v1 2 + kind: Certificate 3 + metadata: 4 + name: loom-sans-self-org 5 + namespace: ci 6 + spec: 7 + secretName: loom-sans-self-org-tls 8 + issuerRef: 9 + name: letsencrypt-prod 10 + kind: ClusterIssuer 11 + dnsNames: 12 + - loom.sans-self.org
+1386
k8s/ci/crds/loom.j5t.io_spindlesets.yaml
··· 1 + --- 2 + apiVersion: apiextensions.k8s.io/v1 3 + kind: CustomResourceDefinition 4 + metadata: 5 + annotations: 6 + controller-gen.kubebuilder.io/version: v0.18.0 7 + name: spindlesets.loom.j5t.io 8 + spec: 9 + group: loom.j5t.io 10 + names: 11 + kind: SpindleSet 12 + listKind: SpindleSetList 13 + plural: spindlesets 14 + singular: spindleset 15 + scope: Namespaced 16 + versions: 17 + - additionalPrinterColumns: 18 + - jsonPath: .spec.pipelineRun.pipelineID 19 + name: Pipeline ID 20 + type: string 21 + - jsonPath: .status.phase 22 + name: Phase 23 + type: string 24 + - jsonPath: .status.runningJobs 25 + name: Running 26 + type: integer 27 + - jsonPath: .status.completedJobs 28 + name: Completed 29 + type: integer 30 + - jsonPath: .status.failedJobs 31 + name: Failed 32 + type: integer 33 + - jsonPath: .metadata.creationTimestamp 34 + name: Age 35 + type: date 36 + name: v1alpha1 37 + schema: 38 + openAPIV3Schema: 39 + description: |- 40 + SpindleSet is the Schema for the spindlesets API. 41 + SpindleSet is an internal resource that represents a single pipeline execution. 42 + It groups Jobs for a pipeline run and provides automatic cleanup via owner references. 43 + properties: 44 + apiVersion: 45 + description: |- 46 + APIVersion defines the versioned schema of this representation of an object. 47 + Servers should convert recognized schemas to the latest internal value, and 48 + may reject unrecognized values. 49 + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 50 + type: string 51 + kind: 52 + description: |- 53 + Kind is a string value representing the REST resource this object represents. 54 + Servers may infer this from the endpoint the client submits requests to. 55 + Cannot be updated. 56 + In CamelCase. 57 + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 58 + type: string 59 + metadata: 60 + type: object 61 + spec: 62 + description: |- 63 + SpindleSetSpec defines the desired state of SpindleSet. 64 + SpindleSet is an internal resource created by the Loom engine to manage pipeline executions. 65 + Users do not create SpindleSet resources directly. 66 + properties: 67 + pipelineRun: 68 + description: |- 69 + PipelineRun contains pipeline-specific information for this pipeline execution. 70 + This SpindleSet is ephemeral and represents a single pipeline run. 71 + properties: 72 + cloneCommands: 73 + description: |- 74 + CloneCommands are the git commands to run in the clone init container. 75 + Generated by tangled.org/core/spindle/models.BuildCloneStep(). 76 + These commands are self-contained (include repo URL and commit SHA). 77 + items: 78 + type: string 79 + type: array 80 + pipelineID: 81 + description: PipelineID is the unique identifier for this pipeline 82 + run from the knot. 83 + type: string 84 + secrets: 85 + description: |- 86 + Secrets contains the repository secrets to inject into workflow Jobs. 87 + Retrieved from the secrets vault and stored here for the controller to consume. 88 + items: 89 + description: SecretData represents a single secret key-value 90 + pair for injection into Jobs. 91 + properties: 92 + key: 93 + description: Key is the environment variable name (e.g., 94 + "GITHUB_TOKEN"). 95 + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ 96 + type: string 97 + value: 98 + description: |- 99 + Value is the secret value in plaintext. 100 + This field should only be populated transiently during SpindleSet creation 101 + and consumed immediately by the controller to create a Kubernetes Secret. 102 + type: string 103 + required: 104 + - key 105 + - value 106 + type: object 107 + type: array 108 + skipClone: 109 + description: SkipClone indicates whether to skip the clone init 110 + container entirely. 111 + type: boolean 112 + workflows: 113 + description: Workflows is the list of workflows to execute in 114 + this pipeline. 115 + items: 116 + description: |- 117 + WorkflowSpec defines a workflow to execute as part of a pipeline. 118 + This is the canonical workflow definition that matches the .tangled/workflows/*.yaml format. 119 + properties: 120 + architecture: 121 + description: Architecture is the target architecture for 122 + this workflow (e.g., "amd64", "arm64"). 123 + enum: 124 + - amd64 125 + - arm64 126 + type: string 127 + dependencies: 128 + description: Dependencies specifies external dependencies 129 + for the workflow. 130 + properties: 131 + nixpkgs: 132 + description: Nixpkgs is a list of Nix packages to make 133 + available. 134 + items: 135 + type: string 136 + type: array 137 + type: object 138 + environment: 139 + additionalProperties: 140 + type: string 141 + description: Environment contains workflow-level environment 142 + variables. 143 + type: object 144 + image: 145 + description: Image is the container image to use for executing 146 + the workflow steps. 147 + type: string 148 + name: 149 + description: Name is the workflow filename (e.g., "workflow-amd64.yaml"). 150 + type: string 151 + steps: 152 + description: Steps is the ordered list of steps to execute 153 + in this workflow. 154 + items: 155 + description: WorkflowStep defines a single step in a workflow. 156 + properties: 157 + command: 158 + description: Command is the shell command to execute. 159 + type: string 160 + environment: 161 + additionalProperties: 162 + type: string 163 + description: Environment contains step-specific environment 164 + variables. 165 + type: object 166 + name: 167 + description: Name is the human-readable name of the 168 + step. 169 + type: string 170 + required: 171 + - command 172 + - name 173 + type: object 174 + type: array 175 + when: 176 + description: When defines conditional execution rules for 177 + this workflow. 178 + items: 179 + description: WorkflowWhen defines conditional execution 180 + rules. 181 + properties: 182 + branch: 183 + description: Branch specifies which branches trigger 184 + this workflow. 185 + items: 186 + type: string 187 + type: array 188 + event: 189 + description: Event specifies which events trigger 190 + this workflow (e.g., "push", "pull_request"). 191 + items: 192 + type: string 193 + type: array 194 + type: object 195 + type: array 196 + required: 197 + - architecture 198 + - image 199 + - name 200 + type: object 201 + minItems: 1 202 + type: array 203 + required: 204 + - pipelineID 205 + - workflows 206 + type: object 207 + template: 208 + description: |- 209 + Template is the pod template configuration for jobs in this SpindleSet. 210 + Set internally by the engine from ConfigMap configuration. 211 + properties: 212 + affinity: 213 + description: Affinity defines scheduling constraints for spindle 214 + job pods. 215 + properties: 216 + nodeAffinity: 217 + description: Describes node affinity scheduling rules for 218 + the pod. 219 + properties: 220 + preferredDuringSchedulingIgnoredDuringExecution: 221 + description: |- 222 + The scheduler will prefer to schedule pods to nodes that satisfy 223 + the affinity expressions specified by this field, but it may choose 224 + a node that violates one or more of the expressions. The node that is 225 + most preferred is the one with the greatest sum of weights, i.e. 226 + for each node that meets all of the scheduling requirements (resource 227 + request, requiredDuringScheduling affinity expressions, etc.), 228 + compute a sum by iterating through the elements of this field and adding 229 + "weight" to the sum if the node matches the corresponding matchExpressions; the 230 + node(s) with the highest sum are the most preferred. 231 + items: 232 + description: |- 233 + An empty preferred scheduling term matches all objects with implicit weight 0 234 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). 235 + properties: 236 + preference: 237 + description: A node selector term, associated with 238 + the corresponding weight. 239 + properties: 240 + matchExpressions: 241 + description: A list of node selector requirements 242 + by node's labels. 243 + items: 244 + description: |- 245 + A node selector requirement is a selector that contains values, a key, and an operator 246 + that relates the key and values. 247 + properties: 248 + key: 249 + description: The label key that the selector 250 + applies to. 251 + type: string 252 + operator: 253 + description: |- 254 + Represents a key's relationship to a set of values. 255 + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. 256 + type: string 257 + values: 258 + description: |- 259 + An array of string values. If the operator is In or NotIn, 260 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 261 + the values array must be empty. If the operator is Gt or Lt, the values 262 + array must have a single element, which will be interpreted as an integer. 263 + This array is replaced during a strategic merge patch. 264 + items: 265 + type: string 266 + type: array 267 + x-kubernetes-list-type: atomic 268 + required: 269 + - key 270 + - operator 271 + type: object 272 + type: array 273 + x-kubernetes-list-type: atomic 274 + matchFields: 275 + description: A list of node selector requirements 276 + by node's fields. 277 + items: 278 + description: |- 279 + A node selector requirement is a selector that contains values, a key, and an operator 280 + that relates the key and values. 281 + properties: 282 + key: 283 + description: The label key that the selector 284 + applies to. 285 + type: string 286 + operator: 287 + description: |- 288 + Represents a key's relationship to a set of values. 289 + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. 290 + type: string 291 + values: 292 + description: |- 293 + An array of string values. If the operator is In or NotIn, 294 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 295 + the values array must be empty. If the operator is Gt or Lt, the values 296 + array must have a single element, which will be interpreted as an integer. 297 + This array is replaced during a strategic merge patch. 298 + items: 299 + type: string 300 + type: array 301 + x-kubernetes-list-type: atomic 302 + required: 303 + - key 304 + - operator 305 + type: object 306 + type: array 307 + x-kubernetes-list-type: atomic 308 + type: object 309 + x-kubernetes-map-type: atomic 310 + weight: 311 + description: Weight associated with matching the 312 + corresponding nodeSelectorTerm, in the range 1-100. 313 + format: int32 314 + type: integer 315 + required: 316 + - preference 317 + - weight 318 + type: object 319 + type: array 320 + x-kubernetes-list-type: atomic 321 + requiredDuringSchedulingIgnoredDuringExecution: 322 + description: |- 323 + If the affinity requirements specified by this field are not met at 324 + scheduling time, the pod will not be scheduled onto the node. 325 + If the affinity requirements specified by this field cease to be met 326 + at some point during pod execution (e.g. due to an update), the system 327 + may or may not try to eventually evict the pod from its node. 328 + properties: 329 + nodeSelectorTerms: 330 + description: Required. A list of node selector terms. 331 + The terms are ORed. 332 + items: 333 + description: |- 334 + A null or empty node selector term matches no objects. The requirements of 335 + them are ANDed. 336 + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. 337 + properties: 338 + matchExpressions: 339 + description: A list of node selector requirements 340 + by node's labels. 341 + items: 342 + description: |- 343 + A node selector requirement is a selector that contains values, a key, and an operator 344 + that relates the key and values. 345 + properties: 346 + key: 347 + description: The label key that the selector 348 + applies to. 349 + type: string 350 + operator: 351 + description: |- 352 + Represents a key's relationship to a set of values. 353 + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. 354 + type: string 355 + values: 356 + description: |- 357 + An array of string values. If the operator is In or NotIn, 358 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 359 + the values array must be empty. If the operator is Gt or Lt, the values 360 + array must have a single element, which will be interpreted as an integer. 361 + This array is replaced during a strategic merge patch. 362 + items: 363 + type: string 364 + type: array 365 + x-kubernetes-list-type: atomic 366 + required: 367 + - key 368 + - operator 369 + type: object 370 + type: array 371 + x-kubernetes-list-type: atomic 372 + matchFields: 373 + description: A list of node selector requirements 374 + by node's fields. 375 + items: 376 + description: |- 377 + A node selector requirement is a selector that contains values, a key, and an operator 378 + that relates the key and values. 379 + properties: 380 + key: 381 + description: The label key that the selector 382 + applies to. 383 + type: string 384 + operator: 385 + description: |- 386 + Represents a key's relationship to a set of values. 387 + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. 388 + type: string 389 + values: 390 + description: |- 391 + An array of string values. If the operator is In or NotIn, 392 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 393 + the values array must be empty. If the operator is Gt or Lt, the values 394 + array must have a single element, which will be interpreted as an integer. 395 + This array is replaced during a strategic merge patch. 396 + items: 397 + type: string 398 + type: array 399 + x-kubernetes-list-type: atomic 400 + required: 401 + - key 402 + - operator 403 + type: object 404 + type: array 405 + x-kubernetes-list-type: atomic 406 + type: object 407 + x-kubernetes-map-type: atomic 408 + type: array 409 + x-kubernetes-list-type: atomic 410 + required: 411 + - nodeSelectorTerms 412 + type: object 413 + x-kubernetes-map-type: atomic 414 + type: object 415 + podAffinity: 416 + description: Describes pod affinity scheduling rules (e.g. 417 + co-locate this pod in the same node, zone, etc. as some 418 + other pod(s)). 419 + properties: 420 + preferredDuringSchedulingIgnoredDuringExecution: 421 + description: |- 422 + The scheduler will prefer to schedule pods to nodes that satisfy 423 + the affinity expressions specified by this field, but it may choose 424 + a node that violates one or more of the expressions. The node that is 425 + most preferred is the one with the greatest sum of weights, i.e. 426 + for each node that meets all of the scheduling requirements (resource 427 + request, requiredDuringScheduling affinity expressions, etc.), 428 + compute a sum by iterating through the elements of this field and adding 429 + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the 430 + node(s) with the highest sum are the most preferred. 431 + items: 432 + description: The weights of all of the matched WeightedPodAffinityTerm 433 + fields are added per-node to find the most preferred 434 + node(s) 435 + properties: 436 + podAffinityTerm: 437 + description: Required. A pod affinity term, associated 438 + with the corresponding weight. 439 + properties: 440 + labelSelector: 441 + description: |- 442 + A label query over a set of resources, in this case pods. 443 + If it's null, this PodAffinityTerm matches with no Pods. 444 + properties: 445 + matchExpressions: 446 + description: matchExpressions is a list 447 + of label selector requirements. The requirements 448 + are ANDed. 449 + items: 450 + description: |- 451 + A label selector requirement is a selector that contains values, a key, and an operator that 452 + relates the key and values. 453 + properties: 454 + key: 455 + description: key is the label key 456 + that the selector applies to. 457 + type: string 458 + operator: 459 + description: |- 460 + operator represents a key's relationship to a set of values. 461 + Valid operators are In, NotIn, Exists and DoesNotExist. 462 + type: string 463 + values: 464 + description: |- 465 + values is an array of string values. If the operator is In or NotIn, 466 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 467 + the values array must be empty. This array is replaced during a strategic 468 + merge patch. 469 + items: 470 + type: string 471 + type: array 472 + x-kubernetes-list-type: atomic 473 + required: 474 + - key 475 + - operator 476 + type: object 477 + type: array 478 + x-kubernetes-list-type: atomic 479 + matchLabels: 480 + additionalProperties: 481 + type: string 482 + description: |- 483 + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels 484 + map is equivalent to an element of matchExpressions, whose key field is "key", the 485 + operator is "In", and the values array contains only "value". The requirements are ANDed. 486 + type: object 487 + type: object 488 + x-kubernetes-map-type: atomic 489 + matchLabelKeys: 490 + description: |- 491 + MatchLabelKeys is a set of pod label keys to select which pods will 492 + be taken into consideration. The keys are used to lookup values from the 493 + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` 494 + to select the group of existing pods which pods will be taken into consideration 495 + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming 496 + pod labels will be ignored. The default value is empty. 497 + The same key is forbidden to exist in both matchLabelKeys and labelSelector. 498 + Also, matchLabelKeys cannot be set when labelSelector isn't set. 499 + items: 500 + type: string 501 + type: array 502 + x-kubernetes-list-type: atomic 503 + mismatchLabelKeys: 504 + description: |- 505 + MismatchLabelKeys is a set of pod label keys to select which pods will 506 + be taken into consideration. The keys are used to lookup values from the 507 + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` 508 + to select the group of existing pods which pods will be taken into consideration 509 + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming 510 + pod labels will be ignored. The default value is empty. 511 + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. 512 + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. 513 + items: 514 + type: string 515 + type: array 516 + x-kubernetes-list-type: atomic 517 + namespaceSelector: 518 + description: |- 519 + A label query over the set of namespaces that the term applies to. 520 + The term is applied to the union of the namespaces selected by this field 521 + and the ones listed in the namespaces field. 522 + null selector and null or empty namespaces list means "this pod's namespace". 523 + An empty selector ({}) matches all namespaces. 524 + properties: 525 + matchExpressions: 526 + description: matchExpressions is a list 527 + of label selector requirements. The requirements 528 + are ANDed. 529 + items: 530 + description: |- 531 + A label selector requirement is a selector that contains values, a key, and an operator that 532 + relates the key and values. 533 + properties: 534 + key: 535 + description: key is the label key 536 + that the selector applies to. 537 + type: string 538 + operator: 539 + description: |- 540 + operator represents a key's relationship to a set of values. 541 + Valid operators are In, NotIn, Exists and DoesNotExist. 542 + type: string 543 + values: 544 + description: |- 545 + values is an array of string values. If the operator is In or NotIn, 546 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 547 + the values array must be empty. This array is replaced during a strategic 548 + merge patch. 549 + items: 550 + type: string 551 + type: array 552 + x-kubernetes-list-type: atomic 553 + required: 554 + - key 555 + - operator 556 + type: object 557 + type: array 558 + x-kubernetes-list-type: atomic 559 + matchLabels: 560 + additionalProperties: 561 + type: string 562 + description: |- 563 + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels 564 + map is equivalent to an element of matchExpressions, whose key field is "key", the 565 + operator is "In", and the values array contains only "value". The requirements are ANDed. 566 + type: object 567 + type: object 568 + x-kubernetes-map-type: atomic 569 + namespaces: 570 + description: |- 571 + namespaces specifies a static list of namespace names that the term applies to. 572 + The term is applied to the union of the namespaces listed in this field 573 + and the ones selected by namespaceSelector. 574 + null or empty namespaces list and null namespaceSelector means "this pod's namespace". 575 + items: 576 + type: string 577 + type: array 578 + x-kubernetes-list-type: atomic 579 + topologyKey: 580 + description: |- 581 + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching 582 + the labelSelector in the specified namespaces, where co-located is defined as running on a node 583 + whose value of the label with key topologyKey matches that of any node on which any of the 584 + selected pods is running. 585 + Empty topologyKey is not allowed. 586 + type: string 587 + required: 588 + - topologyKey 589 + type: object 590 + weight: 591 + description: |- 592 + weight associated with matching the corresponding podAffinityTerm, 593 + in the range 1-100. 594 + format: int32 595 + type: integer 596 + required: 597 + - podAffinityTerm 598 + - weight 599 + type: object 600 + type: array 601 + x-kubernetes-list-type: atomic 602 + requiredDuringSchedulingIgnoredDuringExecution: 603 + description: |- 604 + If the affinity requirements specified by this field are not met at 605 + scheduling time, the pod will not be scheduled onto the node. 606 + If the affinity requirements specified by this field cease to be met 607 + at some point during pod execution (e.g. due to a pod label update), the 608 + system may or may not try to eventually evict the pod from its node. 609 + When there are multiple elements, the lists of nodes corresponding to each 610 + podAffinityTerm are intersected, i.e. all terms must be satisfied. 611 + items: 612 + description: |- 613 + Defines a set of pods (namely those matching the labelSelector 614 + relative to the given namespace(s)) that this pod should be 615 + co-located (affinity) or not co-located (anti-affinity) with, 616 + where co-located is defined as running on a node whose value of 617 + the label with key <topologyKey> matches that of any node on which 618 + a pod of the set of pods is running 619 + properties: 620 + labelSelector: 621 + description: |- 622 + A label query over a set of resources, in this case pods. 623 + If it's null, this PodAffinityTerm matches with no Pods. 624 + properties: 625 + matchExpressions: 626 + description: matchExpressions is a list of label 627 + selector requirements. The requirements are 628 + ANDed. 629 + items: 630 + description: |- 631 + A label selector requirement is a selector that contains values, a key, and an operator that 632 + relates the key and values. 633 + properties: 634 + key: 635 + description: key is the label key that 636 + the selector applies to. 637 + type: string 638 + operator: 639 + description: |- 640 + operator represents a key's relationship to a set of values. 641 + Valid operators are In, NotIn, Exists and DoesNotExist. 642 + type: string 643 + values: 644 + description: |- 645 + values is an array of string values. If the operator is In or NotIn, 646 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 647 + the values array must be empty. This array is replaced during a strategic 648 + merge patch. 649 + items: 650 + type: string 651 + type: array 652 + x-kubernetes-list-type: atomic 653 + required: 654 + - key 655 + - operator 656 + type: object 657 + type: array 658 + x-kubernetes-list-type: atomic 659 + matchLabels: 660 + additionalProperties: 661 + type: string 662 + description: |- 663 + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels 664 + map is equivalent to an element of matchExpressions, whose key field is "key", the 665 + operator is "In", and the values array contains only "value". The requirements are ANDed. 666 + type: object 667 + type: object 668 + x-kubernetes-map-type: atomic 669 + matchLabelKeys: 670 + description: |- 671 + MatchLabelKeys is a set of pod label keys to select which pods will 672 + be taken into consideration. The keys are used to lookup values from the 673 + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` 674 + to select the group of existing pods which pods will be taken into consideration 675 + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming 676 + pod labels will be ignored. The default value is empty. 677 + The same key is forbidden to exist in both matchLabelKeys and labelSelector. 678 + Also, matchLabelKeys cannot be set when labelSelector isn't set. 679 + items: 680 + type: string 681 + type: array 682 + x-kubernetes-list-type: atomic 683 + mismatchLabelKeys: 684 + description: |- 685 + MismatchLabelKeys is a set of pod label keys to select which pods will 686 + be taken into consideration. The keys are used to lookup values from the 687 + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` 688 + to select the group of existing pods which pods will be taken into consideration 689 + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming 690 + pod labels will be ignored. The default value is empty. 691 + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. 692 + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. 693 + items: 694 + type: string 695 + type: array 696 + x-kubernetes-list-type: atomic 697 + namespaceSelector: 698 + description: |- 699 + A label query over the set of namespaces that the term applies to. 700 + The term is applied to the union of the namespaces selected by this field 701 + and the ones listed in the namespaces field. 702 + null selector and null or empty namespaces list means "this pod's namespace". 703 + An empty selector ({}) matches all namespaces. 704 + properties: 705 + matchExpressions: 706 + description: matchExpressions is a list of label 707 + selector requirements. The requirements are 708 + ANDed. 709 + items: 710 + description: |- 711 + A label selector requirement is a selector that contains values, a key, and an operator that 712 + relates the key and values. 713 + properties: 714 + key: 715 + description: key is the label key that 716 + the selector applies to. 717 + type: string 718 + operator: 719 + description: |- 720 + operator represents a key's relationship to a set of values. 721 + Valid operators are In, NotIn, Exists and DoesNotExist. 722 + type: string 723 + values: 724 + description: |- 725 + values is an array of string values. If the operator is In or NotIn, 726 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 727 + the values array must be empty. This array is replaced during a strategic 728 + merge patch. 729 + items: 730 + type: string 731 + type: array 732 + x-kubernetes-list-type: atomic 733 + required: 734 + - key 735 + - operator 736 + type: object 737 + type: array 738 + x-kubernetes-list-type: atomic 739 + matchLabels: 740 + additionalProperties: 741 + type: string 742 + description: |- 743 + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels 744 + map is equivalent to an element of matchExpressions, whose key field is "key", the 745 + operator is "In", and the values array contains only "value". The requirements are ANDed. 746 + type: object 747 + type: object 748 + x-kubernetes-map-type: atomic 749 + namespaces: 750 + description: |- 751 + namespaces specifies a static list of namespace names that the term applies to. 752 + The term is applied to the union of the namespaces listed in this field 753 + and the ones selected by namespaceSelector. 754 + null or empty namespaces list and null namespaceSelector means "this pod's namespace". 755 + items: 756 + type: string 757 + type: array 758 + x-kubernetes-list-type: atomic 759 + topologyKey: 760 + description: |- 761 + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching 762 + the labelSelector in the specified namespaces, where co-located is defined as running on a node 763 + whose value of the label with key topologyKey matches that of any node on which any of the 764 + selected pods is running. 765 + Empty topologyKey is not allowed. 766 + type: string 767 + required: 768 + - topologyKey 769 + type: object 770 + type: array 771 + x-kubernetes-list-type: atomic 772 + type: object 773 + podAntiAffinity: 774 + description: Describes pod anti-affinity scheduling rules 775 + (e.g. avoid putting this pod in the same node, zone, etc. 776 + as some other pod(s)). 777 + properties: 778 + preferredDuringSchedulingIgnoredDuringExecution: 779 + description: |- 780 + The scheduler will prefer to schedule pods to nodes that satisfy 781 + the anti-affinity expressions specified by this field, but it may choose 782 + a node that violates one or more of the expressions. The node that is 783 + most preferred is the one with the greatest sum of weights, i.e. 784 + for each node that meets all of the scheduling requirements (resource 785 + request, requiredDuringScheduling anti-affinity expressions, etc.), 786 + compute a sum by iterating through the elements of this field and subtracting 787 + "weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the 788 + node(s) with the highest sum are the most preferred. 789 + items: 790 + description: The weights of all of the matched WeightedPodAffinityTerm 791 + fields are added per-node to find the most preferred 792 + node(s) 793 + properties: 794 + podAffinityTerm: 795 + description: Required. A pod affinity term, associated 796 + with the corresponding weight. 797 + properties: 798 + labelSelector: 799 + description: |- 800 + A label query over a set of resources, in this case pods. 801 + If it's null, this PodAffinityTerm matches with no Pods. 802 + properties: 803 + matchExpressions: 804 + description: matchExpressions is a list 805 + of label selector requirements. The requirements 806 + are ANDed. 807 + items: 808 + description: |- 809 + A label selector requirement is a selector that contains values, a key, and an operator that 810 + relates the key and values. 811 + properties: 812 + key: 813 + description: key is the label key 814 + that the selector applies to. 815 + type: string 816 + operator: 817 + description: |- 818 + operator represents a key's relationship to a set of values. 819 + Valid operators are In, NotIn, Exists and DoesNotExist. 820 + type: string 821 + values: 822 + description: |- 823 + values is an array of string values. If the operator is In or NotIn, 824 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 825 + the values array must be empty. This array is replaced during a strategic 826 + merge patch. 827 + items: 828 + type: string 829 + type: array 830 + x-kubernetes-list-type: atomic 831 + required: 832 + - key 833 + - operator 834 + type: object 835 + type: array 836 + x-kubernetes-list-type: atomic 837 + matchLabels: 838 + additionalProperties: 839 + type: string 840 + description: |- 841 + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels 842 + map is equivalent to an element of matchExpressions, whose key field is "key", the 843 + operator is "In", and the values array contains only "value". The requirements are ANDed. 844 + type: object 845 + type: object 846 + x-kubernetes-map-type: atomic 847 + matchLabelKeys: 848 + description: |- 849 + MatchLabelKeys is a set of pod label keys to select which pods will 850 + be taken into consideration. The keys are used to lookup values from the 851 + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` 852 + to select the group of existing pods which pods will be taken into consideration 853 + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming 854 + pod labels will be ignored. The default value is empty. 855 + The same key is forbidden to exist in both matchLabelKeys and labelSelector. 856 + Also, matchLabelKeys cannot be set when labelSelector isn't set. 857 + items: 858 + type: string 859 + type: array 860 + x-kubernetes-list-type: atomic 861 + mismatchLabelKeys: 862 + description: |- 863 + MismatchLabelKeys is a set of pod label keys to select which pods will 864 + be taken into consideration. The keys are used to lookup values from the 865 + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` 866 + to select the group of existing pods which pods will be taken into consideration 867 + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming 868 + pod labels will be ignored. The default value is empty. 869 + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. 870 + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. 871 + items: 872 + type: string 873 + type: array 874 + x-kubernetes-list-type: atomic 875 + namespaceSelector: 876 + description: |- 877 + A label query over the set of namespaces that the term applies to. 878 + The term is applied to the union of the namespaces selected by this field 879 + and the ones listed in the namespaces field. 880 + null selector and null or empty namespaces list means "this pod's namespace". 881 + An empty selector ({}) matches all namespaces. 882 + properties: 883 + matchExpressions: 884 + description: matchExpressions is a list 885 + of label selector requirements. The requirements 886 + are ANDed. 887 + items: 888 + description: |- 889 + A label selector requirement is a selector that contains values, a key, and an operator that 890 + relates the key and values. 891 + properties: 892 + key: 893 + description: key is the label key 894 + that the selector applies to. 895 + type: string 896 + operator: 897 + description: |- 898 + operator represents a key's relationship to a set of values. 899 + Valid operators are In, NotIn, Exists and DoesNotExist. 900 + type: string 901 + values: 902 + description: |- 903 + values is an array of string values. If the operator is In or NotIn, 904 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 905 + the values array must be empty. This array is replaced during a strategic 906 + merge patch. 907 + items: 908 + type: string 909 + type: array 910 + x-kubernetes-list-type: atomic 911 + required: 912 + - key 913 + - operator 914 + type: object 915 + type: array 916 + x-kubernetes-list-type: atomic 917 + matchLabels: 918 + additionalProperties: 919 + type: string 920 + description: |- 921 + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels 922 + map is equivalent to an element of matchExpressions, whose key field is "key", the 923 + operator is "In", and the values array contains only "value". The requirements are ANDed. 924 + type: object 925 + type: object 926 + x-kubernetes-map-type: atomic 927 + namespaces: 928 + description: |- 929 + namespaces specifies a static list of namespace names that the term applies to. 930 + The term is applied to the union of the namespaces listed in this field 931 + and the ones selected by namespaceSelector. 932 + null or empty namespaces list and null namespaceSelector means "this pod's namespace". 933 + items: 934 + type: string 935 + type: array 936 + x-kubernetes-list-type: atomic 937 + topologyKey: 938 + description: |- 939 + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching 940 + the labelSelector in the specified namespaces, where co-located is defined as running on a node 941 + whose value of the label with key topologyKey matches that of any node on which any of the 942 + selected pods is running. 943 + Empty topologyKey is not allowed. 944 + type: string 945 + required: 946 + - topologyKey 947 + type: object 948 + weight: 949 + description: |- 950 + weight associated with matching the corresponding podAffinityTerm, 951 + in the range 1-100. 952 + format: int32 953 + type: integer 954 + required: 955 + - podAffinityTerm 956 + - weight 957 + type: object 958 + type: array 959 + x-kubernetes-list-type: atomic 960 + requiredDuringSchedulingIgnoredDuringExecution: 961 + description: |- 962 + If the anti-affinity requirements specified by this field are not met at 963 + scheduling time, the pod will not be scheduled onto the node. 964 + If the anti-affinity requirements specified by this field cease to be met 965 + at some point during pod execution (e.g. due to a pod label update), the 966 + system may or may not try to eventually evict the pod from its node. 967 + When there are multiple elements, the lists of nodes corresponding to each 968 + podAffinityTerm are intersected, i.e. all terms must be satisfied. 969 + items: 970 + description: |- 971 + Defines a set of pods (namely those matching the labelSelector 972 + relative to the given namespace(s)) that this pod should be 973 + co-located (affinity) or not co-located (anti-affinity) with, 974 + where co-located is defined as running on a node whose value of 975 + the label with key <topologyKey> matches that of any node on which 976 + a pod of the set of pods is running 977 + properties: 978 + labelSelector: 979 + description: |- 980 + A label query over a set of resources, in this case pods. 981 + If it's null, this PodAffinityTerm matches with no Pods. 982 + properties: 983 + matchExpressions: 984 + description: matchExpressions is a list of label 985 + selector requirements. The requirements are 986 + ANDed. 987 + items: 988 + description: |- 989 + A label selector requirement is a selector that contains values, a key, and an operator that 990 + relates the key and values. 991 + properties: 992 + key: 993 + description: key is the label key that 994 + the selector applies to. 995 + type: string 996 + operator: 997 + description: |- 998 + operator represents a key's relationship to a set of values. 999 + Valid operators are In, NotIn, Exists and DoesNotExist. 1000 + type: string 1001 + values: 1002 + description: |- 1003 + values is an array of string values. If the operator is In or NotIn, 1004 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 1005 + the values array must be empty. This array is replaced during a strategic 1006 + merge patch. 1007 + items: 1008 + type: string 1009 + type: array 1010 + x-kubernetes-list-type: atomic 1011 + required: 1012 + - key 1013 + - operator 1014 + type: object 1015 + type: array 1016 + x-kubernetes-list-type: atomic 1017 + matchLabels: 1018 + additionalProperties: 1019 + type: string 1020 + description: |- 1021 + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels 1022 + map is equivalent to an element of matchExpressions, whose key field is "key", the 1023 + operator is "In", and the values array contains only "value". The requirements are ANDed. 1024 + type: object 1025 + type: object 1026 + x-kubernetes-map-type: atomic 1027 + matchLabelKeys: 1028 + description: |- 1029 + MatchLabelKeys is a set of pod label keys to select which pods will 1030 + be taken into consideration. The keys are used to lookup values from the 1031 + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` 1032 + to select the group of existing pods which pods will be taken into consideration 1033 + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming 1034 + pod labels will be ignored. The default value is empty. 1035 + The same key is forbidden to exist in both matchLabelKeys and labelSelector. 1036 + Also, matchLabelKeys cannot be set when labelSelector isn't set. 1037 + items: 1038 + type: string 1039 + type: array 1040 + x-kubernetes-list-type: atomic 1041 + mismatchLabelKeys: 1042 + description: |- 1043 + MismatchLabelKeys is a set of pod label keys to select which pods will 1044 + be taken into consideration. The keys are used to lookup values from the 1045 + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` 1046 + to select the group of existing pods which pods will be taken into consideration 1047 + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming 1048 + pod labels will be ignored. The default value is empty. 1049 + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. 1050 + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. 1051 + items: 1052 + type: string 1053 + type: array 1054 + x-kubernetes-list-type: atomic 1055 + namespaceSelector: 1056 + description: |- 1057 + A label query over the set of namespaces that the term applies to. 1058 + The term is applied to the union of the namespaces selected by this field 1059 + and the ones listed in the namespaces field. 1060 + null selector and null or empty namespaces list means "this pod's namespace". 1061 + An empty selector ({}) matches all namespaces. 1062 + properties: 1063 + matchExpressions: 1064 + description: matchExpressions is a list of label 1065 + selector requirements. The requirements are 1066 + ANDed. 1067 + items: 1068 + description: |- 1069 + A label selector requirement is a selector that contains values, a key, and an operator that 1070 + relates the key and values. 1071 + properties: 1072 + key: 1073 + description: key is the label key that 1074 + the selector applies to. 1075 + type: string 1076 + operator: 1077 + description: |- 1078 + operator represents a key's relationship to a set of values. 1079 + Valid operators are In, NotIn, Exists and DoesNotExist. 1080 + type: string 1081 + values: 1082 + description: |- 1083 + values is an array of string values. If the operator is In or NotIn, 1084 + the values array must be non-empty. If the operator is Exists or DoesNotExist, 1085 + the values array must be empty. This array is replaced during a strategic 1086 + merge patch. 1087 + items: 1088 + type: string 1089 + type: array 1090 + x-kubernetes-list-type: atomic 1091 + required: 1092 + - key 1093 + - operator 1094 + type: object 1095 + type: array 1096 + x-kubernetes-list-type: atomic 1097 + matchLabels: 1098 + additionalProperties: 1099 + type: string 1100 + description: |- 1101 + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels 1102 + map is equivalent to an element of matchExpressions, whose key field is "key", the 1103 + operator is "In", and the values array contains only "value". The requirements are ANDed. 1104 + type: object 1105 + type: object 1106 + x-kubernetes-map-type: atomic 1107 + namespaces: 1108 + description: |- 1109 + namespaces specifies a static list of namespace names that the term applies to. 1110 + The term is applied to the union of the namespaces listed in this field 1111 + and the ones selected by namespaceSelector. 1112 + null or empty namespaces list and null namespaceSelector means "this pod's namespace". 1113 + items: 1114 + type: string 1115 + type: array 1116 + x-kubernetes-list-type: atomic 1117 + topologyKey: 1118 + description: |- 1119 + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching 1120 + the labelSelector in the specified namespaces, where co-located is defined as running on a node 1121 + whose value of the label with key topologyKey matches that of any node on which any of the 1122 + selected pods is running. 1123 + Empty topologyKey is not allowed. 1124 + type: string 1125 + required: 1126 + - topologyKey 1127 + type: object 1128 + type: array 1129 + x-kubernetes-list-type: atomic 1130 + type: object 1131 + type: object 1132 + registryCredentialsSecret: 1133 + description: |- 1134 + RegistryCredentialsSecret is the name of a kubernetes.io/dockerconfigjson secret 1135 + containing registry credentials for buildah to use when pushing images. 1136 + If specified, the secret is mounted at /home/user/.docker/config.json. 1137 + type: string 1138 + resourceProfiles: 1139 + description: |- 1140 + ResourceProfiles is an ordered list of resource configurations based on node labels. 1141 + When creating a job, the first profile matching the workflow's architecture is selected. 1142 + The profile's nodeSelector and resources are applied to the job pod. 1143 + items: 1144 + description: |- 1145 + ResourceProfile defines a resource configuration for spindle jobs based on node labels. 1146 + Profiles are matched against workflow architecture and applied to job pods. 1147 + properties: 1148 + nodeSelector: 1149 + additionalProperties: 1150 + type: string 1151 + description: |- 1152 + NodeSelector defines labels that must match for this profile to be used. 1153 + Must include kubernetes.io/arch to match workflow architecture. 1154 + Additional labels allow differentiation between node types (e.g., node-tier, instance-type). 1155 + type: object 1156 + resources: 1157 + description: Resources defines the compute resource requirements 1158 + for jobs using this profile. 1159 + properties: 1160 + claims: 1161 + description: |- 1162 + Claims lists the names of resources, defined in spec.resourceClaims, 1163 + that are used by this container. 1164 + 1165 + This field depends on the 1166 + DynamicResourceAllocation feature gate. 1167 + 1168 + This field is immutable. It can only be set for containers. 1169 + items: 1170 + description: ResourceClaim references one entry in 1171 + PodSpec.ResourceClaims. 1172 + properties: 1173 + name: 1174 + description: |- 1175 + Name must match the name of one entry in pod.spec.resourceClaims of 1176 + the Pod where this field is used. It makes that resource available 1177 + inside a container. 1178 + type: string 1179 + request: 1180 + description: |- 1181 + Request is the name chosen for a request in the referenced claim. 1182 + If empty, everything from the claim is made available, otherwise 1183 + only the result of this request. 1184 + type: string 1185 + required: 1186 + - name 1187 + type: object 1188 + type: array 1189 + x-kubernetes-list-map-keys: 1190 + - name 1191 + x-kubernetes-list-type: map 1192 + limits: 1193 + additionalProperties: 1194 + anyOf: 1195 + - type: integer 1196 + - type: string 1197 + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 1198 + x-kubernetes-int-or-string: true 1199 + description: |- 1200 + Limits describes the maximum amount of compute resources allowed. 1201 + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 1202 + type: object 1203 + requests: 1204 + additionalProperties: 1205 + anyOf: 1206 + - type: integer 1207 + - type: string 1208 + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 1209 + x-kubernetes-int-or-string: true 1210 + description: |- 1211 + Requests describes the minimum amount of compute resources required. 1212 + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, 1213 + otherwise to an implementation-defined value. Requests cannot exceed Limits. 1214 + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 1215 + type: object 1216 + type: object 1217 + required: 1218 + - nodeSelector 1219 + - resources 1220 + type: object 1221 + type: array 1222 + tolerations: 1223 + description: Tolerations allows pods to schedule onto nodes with 1224 + matching taints. 1225 + items: 1226 + description: |- 1227 + The pod this Toleration is attached to tolerates any taint that matches 1228 + the triple <key,value,effect> using the matching operator <operator>. 1229 + properties: 1230 + effect: 1231 + description: |- 1232 + Effect indicates the taint effect to match. Empty means match all taint effects. 1233 + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. 1234 + type: string 1235 + key: 1236 + description: |- 1237 + Key is the taint key that the toleration applies to. Empty means match all taint keys. 1238 + If the key is empty, operator must be Exists; this combination means to match all values and all keys. 1239 + type: string 1240 + operator: 1241 + description: |- 1242 + Operator represents a key's relationship to the value. 1243 + Valid operators are Exists and Equal. Defaults to Equal. 1244 + Exists is equivalent to wildcard for value, so that a pod can 1245 + tolerate all taints of a particular category. 1246 + type: string 1247 + tolerationSeconds: 1248 + description: |- 1249 + TolerationSeconds represents the period of time the toleration (which must be 1250 + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, 1251 + it is not set, which means tolerate the taint forever (do not evict). Zero and 1252 + negative values will be treated as 0 (evict immediately) by the system. 1253 + format: int64 1254 + type: integer 1255 + value: 1256 + description: |- 1257 + Value is the taint value the toleration matches to. 1258 + If the operator is Exists, the value should be empty, otherwise just a regular string. 1259 + type: string 1260 + type: object 1261 + type: array 1262 + type: object 1263 + required: 1264 + - pipelineRun 1265 + type: object 1266 + status: 1267 + description: SpindleSetStatus defines the observed state of SpindleSet. 1268 + properties: 1269 + completedJobs: 1270 + description: CompletedJobs is the total number of spindle jobs that 1271 + have completed successfully. 1272 + format: int32 1273 + type: integer 1274 + conditions: 1275 + description: Conditions represent the latest available observations 1276 + of the SpindleSet's state. 1277 + items: 1278 + description: Condition contains details for one aspect of the current 1279 + state of this API Resource. 1280 + properties: 1281 + lastTransitionTime: 1282 + description: |- 1283 + lastTransitionTime is the last time the condition transitioned from one status to another. 1284 + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. 1285 + format: date-time 1286 + type: string 1287 + message: 1288 + description: |- 1289 + message is a human readable message indicating details about the transition. 1290 + This may be an empty string. 1291 + maxLength: 32768 1292 + type: string 1293 + observedGeneration: 1294 + description: |- 1295 + observedGeneration represents the .metadata.generation that the condition was set based upon. 1296 + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date 1297 + with respect to the current state of the instance. 1298 + format: int64 1299 + minimum: 0 1300 + type: integer 1301 + reason: 1302 + description: |- 1303 + reason contains a programmatic identifier indicating the reason for the condition's last transition. 1304 + Producers of specific condition types may define expected values and meanings for this field, 1305 + and whether the values are considered a guaranteed API. 1306 + The value should be a CamelCase string. 1307 + This field may not be empty. 1308 + maxLength: 1024 1309 + minLength: 1 1310 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ 1311 + type: string 1312 + status: 1313 + description: status of the condition, one of True, False, Unknown. 1314 + enum: 1315 + - "True" 1316 + - "False" 1317 + - Unknown 1318 + type: string 1319 + type: 1320 + description: type of condition in CamelCase or in foo.example.com/CamelCase. 1321 + maxLength: 316 1322 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ 1323 + type: string 1324 + required: 1325 + - lastTransitionTime 1326 + - message 1327 + - reason 1328 + - status 1329 + - type 1330 + type: object 1331 + type: array 1332 + x-kubernetes-list-map-keys: 1333 + - type 1334 + x-kubernetes-list-type: map 1335 + failedJobs: 1336 + description: FailedJobs is the total number of spindle jobs that have 1337 + failed. 1338 + format: int32 1339 + type: integer 1340 + pendingJobs: 1341 + description: PendingJobs is the number of spindle jobs currently pending. 1342 + format: int32 1343 + type: integer 1344 + phase: 1345 + description: Phase represents the current phase of the pipeline execution. 1346 + type: string 1347 + runningJobs: 1348 + description: RunningJobs is the number of spindle jobs currently running. 1349 + format: int32 1350 + type: integer 1351 + workflowStatuses: 1352 + description: WorkflowStatuses tracks the status of individual workflows 1353 + in a pipeline run. 1354 + items: 1355 + description: WorkflowStatus tracks the status of a single workflow 1356 + execution. 1357 + properties: 1358 + completionTime: 1359 + description: CompletionTime is when the workflow finished. 1360 + format: date-time 1361 + type: string 1362 + jobName: 1363 + description: JobName is the name of the Kubernetes Job created 1364 + for this workflow. 1365 + type: string 1366 + name: 1367 + description: Name is the workflow name. 1368 + type: string 1369 + phase: 1370 + description: Phase is the current phase of the workflow (Pending, 1371 + Running, Succeeded, Failed). 1372 + type: string 1373 + startTime: 1374 + description: StartTime is when the workflow started executing. 1375 + format: date-time 1376 + type: string 1377 + required: 1378 + - name 1379 + type: object 1380 + type: array 1381 + type: object 1382 + type: object 1383 + served: true 1384 + storage: true 1385 + subresources: 1386 + status: {}
+28
k8s/ci/ingress.yaml
··· 1 + apiVersion: traefik.io/v1alpha1 2 + kind: Middleware 3 + metadata: 4 + name: strip-server-headers 5 + namespace: ci 6 + spec: 7 + headers: 8 + customResponseHeaders: 9 + X-Powered-By: "" 10 + --- 11 + apiVersion: traefik.io/v1alpha1 12 + kind: IngressRoute 13 + metadata: 14 + name: loom 15 + namespace: ci 16 + spec: 17 + entryPoints: 18 + - websecure 19 + tls: 20 + secretName: loom-sans-self-org-tls 21 + routes: 22 + - match: Host(`loom.sans-self.org`) 23 + kind: Rule 24 + middlewares: 25 + - name: strip-server-headers 26 + services: 27 + - name: loom 28 + port: 6555
+12
k8s/ci/kustomization.yaml
··· 6 6 7 7 resources: 8 8 - namespace.yaml 9 + # Loom CI operator 10 + - crds/loom.j5t.io_spindlesets.yaml 11 + - loom-deployment.yaml 12 + - loom-rbac.yaml 13 + - loom-config.yaml 14 + - loom-env.yaml 15 + - loom-pvc.yaml 16 + - loom-service.yaml 17 + - loom-network-policy.yaml 18 + - cert.yaml 19 + - ingress.yaml 20 + # Fallback: custom listener for raw k8s Job specs 9 21 - deployment.yaml 10 22 - rbac.yaml 11 23
+2 -2
k8s/ci/listener.py
··· 85 85 log(f" {name}: bad yaml: {e}") 86 86 continue 87 87 88 - if parsed.get("engine") != "kubernetes": 88 + if parsed.get("engine") != "kubernetes-job": 89 89 continue 90 90 91 91 job_spec = parsed.get("job") 92 92 if not job_spec: 93 - log(f" {name}: engine=kubernetes but no 'job' field") 93 + log(f" {name}: engine=kubernetes-job but no 'job' field") 94 94 continue 95 95 96 96 repo_name = repo.get("repo", "unknown")
+19
k8s/ci/loom-config.yaml
··· 1 + apiVersion: v1 2 + kind: ConfigMap 3 + metadata: 4 + name: loom-config 5 + namespace: ci 6 + data: 7 + config.yaml: | 8 + maxConcurrentJobs: 4 9 + template: 10 + resourceProfiles: 11 + - nodeSelector: 12 + kubernetes.io/arch: arm64 13 + resources: 14 + requests: 15 + cpu: "100m" 16 + memory: "128Mi" 17 + limits: 18 + cpu: "1" 19 + memory: "1Gi"
+104
k8s/ci/loom-deployment.yaml
··· 1 + apiVersion: apps/v1 2 + kind: Deployment 3 + metadata: 4 + name: loom 5 + namespace: ci 6 + labels: 7 + app: loom 8 + control-plane: controller-manager 9 + spec: 10 + replicas: 1 11 + selector: 12 + matchLabels: 13 + app: loom 14 + control-plane: controller-manager 15 + # RWO PVCs — only one pod can mount at a time 16 + strategy: 17 + type: Recreate 18 + template: 19 + metadata: 20 + annotations: 21 + kubectl.kubernetes.io/default-container: manager 22 + labels: 23 + app: loom 24 + control-plane: controller-manager 25 + spec: 26 + serviceAccountName: loom-controller 27 + terminationGracePeriodSeconds: 10 28 + securityContext: 29 + runAsNonRoot: true 30 + runAsUser: 65532 31 + fsGroup: 65532 32 + seccompProfile: 33 + type: RuntimeDefault 34 + containers: 35 + - name: manager 36 + image: zot.sans-self.org/infra/loom:latest 37 + imagePullPolicy: Always 38 + command: 39 + - /manager 40 + args: 41 + - --health-probe-bind-address=:8081 42 + env: 43 + - name: POD_NAMESPACE 44 + valueFrom: 45 + fieldRef: 46 + fieldPath: metadata.namespace 47 + - name: LOOM_IMAGE 48 + value: zot.sans-self.org/infra/loom:latest 49 + - name: SPINDLE_SERVER_HOSTNAME 50 + valueFrom: 51 + configMapKeyRef: 52 + name: loom-env 53 + key: hostname 54 + - name: SPINDLE_SERVER_OWNER 55 + valueFrom: 56 + configMapKeyRef: 57 + name: loom-env 58 + key: owner 59 + - name: SPINDLE_SERVER_DB_PATH 60 + value: /data/spindle.db 61 + - name: SPINDLE_SERVER_LOG_DIR 62 + value: /tmp/spindle-logs 63 + securityContext: 64 + allowPrivilegeEscalation: false 65 + capabilities: 66 + drop: 67 + - ALL 68 + livenessProbe: 69 + httpGet: 70 + path: /healthz 71 + port: 8081 72 + initialDelaySeconds: 15 73 + periodSeconds: 20 74 + readinessProbe: 75 + httpGet: 76 + path: /readyz 77 + port: 8081 78 + initialDelaySeconds: 5 79 + periodSeconds: 10 80 + resources: 81 + requests: 82 + cpu: 50m 83 + memory: 128Mi 84 + limits: 85 + cpu: 500m 86 + memory: 512Mi 87 + volumeMounts: 88 + - name: spindle-logs 89 + mountPath: /tmp/spindle-logs 90 + - name: spindle-db 91 + mountPath: /data 92 + - name: loom-config 93 + mountPath: /etc/loom 94 + readOnly: true 95 + volumes: 96 + - name: spindle-logs 97 + persistentVolumeClaim: 98 + claimName: loom-spindle-logs 99 + - name: spindle-db 100 + persistentVolumeClaim: 101 + claimName: loom-spindle-db 102 + - name: loom-config 103 + configMap: 104 + name: loom-config
+10
k8s/ci/loom-env.yaml
··· 1 + apiVersion: v1 2 + kind: ConfigMap 3 + metadata: 4 + name: loom-env 5 + namespace: ci 6 + data: 7 + # Spindle's did:web identity — needs DNS + IngressRoute to be externally reachable 8 + hostname: loom.sans-self.org 9 + # Owner's AT Proto DID 10 + owner: "did:plc:wydyrngmxbcsqdvhmd7whmye"
+34
k8s/ci/loom-network-policy.yaml
··· 1 + # Controller egress intentionally omitted — needs broad access: 2 + # k8s API (ClusterIP), Jetstream (external WS), knot (internal WS), DNS. 3 + # k3s iptables doesn't reliably match ClusterIP traffic with port-only rules. 4 + # 5 + # CI job pods: allow outbound internet (image pulls, Kaniko push, wget) 6 + # but no inbound traffic 7 + apiVersion: networking.k8s.io/v1 8 + kind: NetworkPolicy 9 + metadata: 10 + name: ci-job-default 11 + namespace: ci 12 + spec: 13 + podSelector: 14 + matchLabels: 15 + loom.j5t.io/component: spindle 16 + policyTypes: 17 + - Ingress 18 + - Egress 19 + ingress: [] 20 + egress: 21 + # DNS 22 + - ports: 23 + - port: 53 24 + protocol: UDP 25 + - port: 53 26 + protocol: TCP 27 + # HTTPS (registries, APIs) 28 + - ports: 29 + - port: 443 30 + protocol: TCP 31 + # HTTP (some registries, wget targets) 32 + - ports: 33 + - port: 80 34 + protocol: TCP
+25
k8s/ci/loom-pvc.yaml
··· 1 + apiVersion: v1 2 + kind: PersistentVolumeClaim 3 + metadata: 4 + name: loom-spindle-db 5 + namespace: ci 6 + spec: 7 + accessModes: 8 + - ReadWriteOnce 9 + storageClassName: juicefs-sc 10 + resources: 11 + requests: 12 + storage: 1Gi 13 + --- 14 + apiVersion: v1 15 + kind: PersistentVolumeClaim 16 + metadata: 17 + name: loom-spindle-logs 18 + namespace: ci 19 + spec: 20 + accessModes: 21 + - ReadWriteOnce 22 + storageClassName: juicefs-sc 23 + resources: 24 + requests: 25 + storage: 5Gi
+91
k8s/ci/loom-rbac.yaml
··· 1 + apiVersion: v1 2 + kind: ServiceAccount 3 + metadata: 4 + name: loom-controller 5 + namespace: ci 6 + --- 7 + # Job pods get a zero-permission SA — they should not touch the k8s API 8 + apiVersion: v1 9 + kind: ServiceAccount 10 + metadata: 11 + name: loom-job 12 + namespace: ci 13 + automountServiceAccountToken: false 14 + --- 15 + # Controller needs cluster-scoped access for CRD watches + node listing 16 + apiVersion: rbac.authorization.k8s.io/v1 17 + kind: ClusterRole 18 + metadata: 19 + name: loom-manager-role 20 + rules: 21 + - apiGroups: [""] 22 + resources: [nodes] 23 + verbs: [list, watch] 24 + - apiGroups: [""] 25 + resources: [pods] 26 + verbs: [get, list, watch] 27 + - apiGroups: [""] 28 + resources: [pods/log] 29 + verbs: [get] 30 + - apiGroups: [""] 31 + resources: [secrets, services] 32 + verbs: [create, delete, get, list, patch, update, watch] 33 + - apiGroups: [batch] 34 + resources: [jobs] 35 + verbs: [create, delete, get, list, patch, update, watch] 36 + - apiGroups: [batch] 37 + resources: [jobs/status] 38 + verbs: [get] 39 + - apiGroups: [loom.j5t.io] 40 + resources: [spindlesets] 41 + verbs: [create, delete, get, list, patch, update, watch] 42 + - apiGroups: [loom.j5t.io] 43 + resources: [spindlesets/finalizers] 44 + verbs: [update] 45 + - apiGroups: [loom.j5t.io] 46 + resources: [spindlesets/status] 47 + verbs: [get, patch, update] 48 + --- 49 + apiVersion: rbac.authorization.k8s.io/v1 50 + kind: ClusterRoleBinding 51 + metadata: 52 + name: loom-manager-rolebinding 53 + roleRef: 54 + apiGroup: rbac.authorization.k8s.io 55 + kind: ClusterRole 56 + name: loom-manager-role 57 + subjects: 58 + - kind: ServiceAccount 59 + name: loom-controller 60 + namespace: ci 61 + --- 62 + # Leader election is namespace-scoped 63 + apiVersion: rbac.authorization.k8s.io/v1 64 + kind: Role 65 + metadata: 66 + name: loom-leader-election 67 + namespace: ci 68 + rules: 69 + - apiGroups: [""] 70 + resources: [configmaps] 71 + verbs: [get, list, watch, create, update, patch, delete] 72 + - apiGroups: [coordination.k8s.io] 73 + resources: [leases] 74 + verbs: [get, list, watch, create, update, patch, delete] 75 + - apiGroups: [""] 76 + resources: [events] 77 + verbs: [create, patch] 78 + --- 79 + apiVersion: rbac.authorization.k8s.io/v1 80 + kind: RoleBinding 81 + metadata: 82 + name: loom-leader-election 83 + namespace: ci 84 + roleRef: 85 + apiGroup: rbac.authorization.k8s.io 86 + kind: Role 87 + name: loom-leader-election 88 + subjects: 89 + - kind: ServiceAccount 90 + name: loom-controller 91 + namespace: ci
+17
k8s/ci/loom-service.yaml
··· 1 + apiVersion: v1 2 + kind: Service 3 + metadata: 4 + name: loom 5 + namespace: ci 6 + spec: 7 + selector: 8 + app: loom 9 + control-plane: controller-manager 10 + ports: 11 + - name: spindle 12 + port: 6555 13 + targetPort: 6555 14 + - name: health 15 + port: 8081 16 + targetPort: 8081 17 + type: ClusterIP