From 43f854932031f3da43290fd98b864602cede5b62 Mon Sep 17 00:00:00 2001 From: allanice001 Date: Sun, 2 Nov 2025 17:18:28 +0000 Subject: [PATCH] feat: mostly terraform shenanigans, but TF can now create ssh keys and servers --- docs/docs.go | 2 +- docs/swagger.json | 2 +- docs/swagger.yaml | 272 +++++ internal/api/routes.go | 9 + internal/app/runtime.go | 1 + internal/handlers/dto/labels.go | 19 + internal/handlers/labels.go | 290 +++++ internal/handlers/ssh.go | 31 +- internal/models/label.go | 18 + internal/models/node_pool.go | 4 +- sdk/go/.openapi-generator/FILES | 9 +- sdk/go/README.md | 8 + sdk/go/api/openapi.yaml | 323 +++++ sdk/go/api_labels.go | 1075 +++++++++++++++++ sdk/go/client.go | 3 + sdk/go/docs/DtoCreateLabelRequest.md | 82 ++ sdk/go/docs/DtoLabelResponse.md | 108 ++ sdk/go/docs/DtoUpdateLabelRequest.md | 82 ++ sdk/go/docs/LabelsAPI.md | 371 ++++++ sdk/go/model_dto_create_label_request.go | 160 +++ sdk/go/model_dto_label_response.go | 196 +++ sdk/go/model_dto_update_label_request.go | 160 +++ sdk/go/test/api_labels_test.go | 91 ++ sdk/ts/.openapi-generator/FILES | 8 + sdk/ts/README.md | 8 + sdk/ts/docs/DtoCreateLabelRequest.md | 32 + sdk/ts/docs/DtoLabelResponse.md | 34 + sdk/ts/docs/DtoUpdateLabelRequest.md | 32 + sdk/ts/docs/LabelsApi.md | 412 +++++++ sdk/ts/src/apis/LabelsApi.ts | 437 +++++++ sdk/ts/src/apis/index.ts | 1 + sdk/ts/src/models/DtoCreateLabelRequest.ts | 80 ++ sdk/ts/src/models/DtoLabelResponse.ts | 86 ++ sdk/ts/src/models/DtoUpdateLabelRequest.ts | 80 ++ sdk/ts/src/models/index.ts | 3 + .../internal/provider/datasource_server.go | 149 +++ .../internal/provider/provider.go | 2 + .../internal/provider/resource_server.go | 382 ++++++ terraform/envs/dev/.terraform.lock.hcl | 2 +- .../envs/dev/.terraform/modules/modules.json | 2 +- terraform/envs/dev/main.tf | 44 + terraform/envs/dev/terraform.tfstate | 2 +- terraform/envs/dev/terraform.tfstate.backup | 2 +- terraform/envs/dev/terraform.tfvars | 16 +- terraform/modules/servers/main.tf | 39 + terraform/modules/servers/outputs.tf | 28 + terraform/modules/servers/variables.tf | 34 + terraform/modules/servers/versions.tf | 9 + ui/src/sdk/.openapi-generator/FILES | 8 + ui/src/sdk/apis/LabelsApi.ts | 359 ++++++ ui/src/sdk/apis/index.ts | 1 + ui/src/sdk/docs/DtoCreateLabelRequest.md | 36 + ui/src/sdk/docs/DtoLabelResponse.md | 38 + ui/src/sdk/docs/DtoUpdateLabelRequest.md | 36 + ui/src/sdk/docs/LabelsApi.md | 433 +++++++ ui/src/sdk/models/DtoCreateLabelRequest.ts | 73 ++ ui/src/sdk/models/DtoLabelResponse.ts | 81 ++ ui/src/sdk/models/DtoUpdateLabelRequest.ts | 73 ++ ui/src/sdk/models/index.ts | 3 + 59 files changed, 6353 insertions(+), 28 deletions(-) create mode 100644 internal/handlers/dto/labels.go create mode 100644 internal/handlers/labels.go create mode 100644 internal/models/label.go create mode 100644 sdk/go/api_labels.go create mode 100644 sdk/go/docs/DtoCreateLabelRequest.md create mode 100644 sdk/go/docs/DtoLabelResponse.md create mode 100644 sdk/go/docs/DtoUpdateLabelRequest.md create mode 100644 sdk/go/docs/LabelsAPI.md create mode 100644 sdk/go/model_dto_create_label_request.go create mode 100644 sdk/go/model_dto_label_response.go create mode 100644 sdk/go/model_dto_update_label_request.go create mode 100644 sdk/go/test/api_labels_test.go create mode 100644 sdk/ts/docs/DtoCreateLabelRequest.md create mode 100644 sdk/ts/docs/DtoLabelResponse.md create mode 100644 sdk/ts/docs/DtoUpdateLabelRequest.md create mode 100644 sdk/ts/docs/LabelsApi.md create mode 100644 sdk/ts/src/apis/LabelsApi.ts create mode 100644 sdk/ts/src/models/DtoCreateLabelRequest.ts create mode 100644 sdk/ts/src/models/DtoLabelResponse.ts create mode 100644 sdk/ts/src/models/DtoUpdateLabelRequest.ts create mode 100644 terraform-provider-autoglue/internal/provider/datasource_server.go create mode 100644 terraform-provider-autoglue/internal/provider/resource_server.go create mode 100644 terraform/modules/servers/main.tf create mode 100644 terraform/modules/servers/outputs.tf create mode 100644 terraform/modules/servers/variables.tf create mode 100644 terraform/modules/servers/versions.tf create mode 100644 ui/src/sdk/apis/LabelsApi.ts create mode 100644 ui/src/sdk/docs/DtoCreateLabelRequest.md create mode 100644 ui/src/sdk/docs/DtoLabelResponse.md create mode 100644 ui/src/sdk/docs/DtoUpdateLabelRequest.md create mode 100644 ui/src/sdk/docs/LabelsApi.md create mode 100644 ui/src/sdk/models/DtoCreateLabelRequest.ts create mode 100644 ui/src/sdk/models/DtoLabelResponse.ts create mode 100644 ui/src/sdk/models/DtoUpdateLabelRequest.ts diff --git a/docs/docs.go b/docs/docs.go index c172431..833b08d 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -5,7 +5,7 @@ package docs import "github.com/swaggo/swag/v2" const docTemplate = `{ - "schemes": {{ marshal .Schemes }},"swagger":"2.0","info":{"description":"{{escape .Description}}","title":"{{.Title}}","contact":{"name":"GlueOps"},"version":"{{.Version}}"},"host":"{{.Host}}","basePath":"{{.BasePath}}","paths":{"/.well-known/jwks.json":{"get":{"description":"Returns the JSON Web Key Set for token verification","produces":["application/json"],"tags":["Auth"],"summary":"Get JWKS","operationId":"getJWKS","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.JWKS"}}}}},"/auth/logout":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Revoke refresh token family (logout everywhere)","operationId":"Logout","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.LogoutRequest"}}],"responses":{"204":{"description":"No Content"}}}},"/auth/refresh":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Rotate refresh token","operationId":"Refresh","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.RefreshRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/callback":{"get":{"produces":["application/json"],"tags":["Auth"],"summary":"Handle social login callback","operationId":"AuthCallback","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/start":{"post":{"description":"Returns provider authorization URL for the frontend to redirect","produces":["application/json"],"tags":["Auth"],"summary":"Begin social login","operationId":"AuthStart","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.AuthStartResponse"}}}}},"/me":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me"],"summary":"Get current user profile","operationId":"GetMe","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.meResponse"}}}},"patch":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Me"],"summary":"Update current user profile","operationId":"UpdateMe","parameters":[{"description":"Patch profile","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.updateMeRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.User"}}}}},"/me/api-keys":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"List my API keys","operationId":"ListUserAPIKeys","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"post":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"description":"Returns the plaintext key once. Store it securely on the client side.","consumes":["application/json"],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Create a new user API key","operationId":"CreateUserAPIKey","parameters":[{"description":"Key options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.createUserKeyRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"/me/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Delete a user API key","operationId":"DeleteUserAPIKey","parameters":[{"type":"string","description":"Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content"}}}},"/orgs":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List organizations I belong to","operationId":"listMyOrgs","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create organization","operationId":"createOrg","parameters":[{"description":"Org payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/models.Organization"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"409":{"description":"Conflict","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Get organization","operationId":"getOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete organization (owner)","operationId":"deleteOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"patch":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Update organization (owner/admin)","operationId":"updateOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Update payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgUpdateReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List org-scoped API keys (no secrets)","operationId":"listOrgKeys","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.APIKey"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create org key/secret pair (owner/admin)","operationId":"createOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Key name + optional expiry","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgKeyCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.orgKeyCreateResp"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys/{key_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete org key (owner/admin)","operationId":"deleteOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"Key ID (UUID)","name":"key_id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List members in org","operationId":"listMembers","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.memberOut"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Add or update a member (owner/admin)","operationId":"addOrUpdateMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"User \u0026 role","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.memberUpsertReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.memberOut"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members/{user_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Remove a member (owner/admin)","operationId":"removeMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"User ID (UUID)","name":"user_id","in":"path","required":true}],"responses":{"204":{"description":"Removed"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/servers":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns servers for the organization in X-Org-ID. Optional filters: status, role.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"List servers (org scoped)","operationId":"ListServers","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Filter by status (pending|provisioning|ready|failed)","name":"status","in":"query"},{"type":"string","description":"Filter by role","name":"role","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.ServerResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list servers","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a server bound to the org in X-Org-ID. Validates that ssh_key_id belongs to the org.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Create server (org scoped)","operationId":"CreateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Server payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateServerRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid json / missing fields / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/servers/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one server in the given organization.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Get server by ID (org scoped)","operationId":"GetServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the server.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Delete server (org scoped)","operationId":"DeleteServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update fields; changing ssh_key_id validates ownership.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Update server (org scoped)","operationId":"UpdateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateServerRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id / invalid json / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/ssh":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns ssh keys for the organization in X-Org-ID.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"List ssh keys (org scoped)","operationId":"ListPublicSshKeys","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.SshResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list keys","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Generates an RSA or ED25519 keypair, saves it, and returns metadata. For RSA you may set bits (2048/3072/4096). Default is 4096. ED25519 ignores bits.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Create ssh keypair (org scoped)","operationId":"CreateSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Key generation options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateSSHRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.SshResponse"}},"400":{"description":"invalid json / invalid bits","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"generation/create failed","schema":{"type":"string"}}}}},"/ssh/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns public key fields. Append ` + "`" + `?reveal=true` + "`" + ` to include the private key PEM.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Get ssh key by ID (org scoped)","operationId":"GetSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"type":"boolean","description":"Reveal private key PEM","name":"reveal","in":"query"}],"responses":{"200":{"description":"When reveal=true","schema":{"$ref":"#/definitions/dto.SshRevealResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes a keypair.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Delete ssh keypair (org scoped)","operationId":"DeleteSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}}},"/ssh/{id}/download":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Download ` + "`" + `part=public|private|both` + "`" + ` of the keypair. ` + "`" + `both` + "`" + ` returns a zip file.","produces":["application/json"],"tags":["Ssh"],"summary":"Download ssh key files by ID (org scoped)","operationId":"DownloadSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header","required":true},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"enum":["public","private","both"],"type":"string","description":"Which part to download","name":"part","in":"query","required":true}],"responses":{"200":{"description":"file content","schema":{"type":"string"}},"400":{"description":"invalid id / invalid part","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"download failed","schema":{"type":"string"}}}}},"/taints":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node taints for the organization in X-Org-ID. Filters: ` + "`" + `key` + "`" + `, ` + "`" + `value` + "`" + `, and ` + "`" + `q` + "`" + ` (key contains). Add ` + "`" + `include=node_pools` + "`" + ` to include linked node pools.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"List node pool taints (org scoped)","operationId":"ListTaints","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.TaintResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Create node taint (org scoped)","operationId":"CreateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Taint payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateTaintRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/taints/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Get node taint by ID (org scoped)","operationId":"GetTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Delete taint (org scoped)","operationId":"DeleteTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update taint fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Update node taint (org scoped)","operationId":"UpdateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateTaintRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}}},"definitions":{"dto.AuthStartResponse":{"type":"object","properties":{"auth_url":{"type":"string","example":"https://accounts.google.com/o/oauth2/v2/auth?client_id=..."}}},"dto.CreateSSHRequest":{"type":"object","properties":{"bits":{"description":"Only for RSA","type":"integer"},"comment":{"type":"string","example":"deploy@autoglue"},"name":{"type":"string"},"type":{"description":"\"rsa\" (default) or \"ed25519\"","type":"string"}}},"dto.CreateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.CreateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.JWK":{"type":"object","properties":{"alg":{"type":"string","example":"RS256"},"e":{"type":"string","example":"AQAB"},"kid":{"type":"string","example":"7c6f1d0a-7a98-4e6a-9dbf-6b1af4b9f345"},"kty":{"type":"string","example":"RSA"},"n":{"type":"string"},"use":{"type":"string","example":"sig"},"x":{"type":"string"}}},"dto.JWKS":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/definitions/dto.JWK"}}}},"dto.LogoutRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.RefreshRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.ServerResponse":{"type":"object","properties":{"created_at":{"type":"string"},"hostname":{"type":"string"},"id":{"type":"string"},"organization_id":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshRevealResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"private_key":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.TaintResponse":{"type":"object","properties":{"effect":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.TokenPair":{"type":"object","properties":{"access_token":{"type":"string","example":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ij..."},"expires_in":{"type":"integer","example":3600},"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf...."},"token_type":{"type":"string","example":"Bearer"}}},"dto.UpdateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.UpdateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"handlers.createUserKeyRequest":{"type":"object","properties":{"expires_in_hours":{"description":"optional TTL","type":"integer"},"name":{"type":"string"}}},"handlers.meResponse":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"emails":{"type":"array","items":{"$ref":"#/definitions/models.UserEmail"}},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"organizations":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"handlers.memberOut":{"type":"object","properties":{"email":{"type":"string"},"role":{"description":"owner/admin/member","type":"string"},"user_id":{"type":"string","format":"uuid"}}},"handlers.memberUpsertReq":{"type":"object","properties":{"role":{"type":"string","example":"member"},"user_id":{"type":"string","format":"uuid"}}},"handlers.orgCreateReq":{"type":"object","properties":{"domain":{"type":"string","example":"acme.com"},"name":{"type":"string","example":"Acme Corp"}}},"handlers.orgKeyCreateReq":{"type":"object","properties":{"expires_in_hours":{"type":"integer","example":720},"name":{"type":"string","example":"automation-bot"}}},"handlers.orgKeyCreateResp":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org_key":{"description":"shown once:","type":"string"},"org_secret":{"description":"shown once:","type":"string"},"scope":{"description":"\"org\"","type":"string"}}},"handlers.orgUpdateReq":{"type":"object","properties":{"domain":{"type":"string"},"name":{"type":"string"}}},"handlers.updateMeRequest":{"type":"object","properties":{"display_name":{"type":"string"}}},"handlers.userAPIKeyOut":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string"},"name":{"type":"string"},"plain":{"description":"Shown only on create:","type":"string"},"scope":{"description":"\"user\"","type":"string"}}},"models.APIKey":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"org_id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"revoked":{"type":"boolean"},"scope":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"user_id":{"type":"string","format":"uuid"}}},"models.Organization":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"domain":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"name":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.User":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.UserEmail":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"email":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_primary":{"type":"boolean"},"is_verified":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"},"user":{"$ref":"#/definitions/models.User"},"user_id":{"type":"string","format":"uuid"}}},"utils.ErrorResponse":{"type":"object","properties":{"code":{"description":"A machine-readable error code, e.g. \"validation_error\"\nexample: validation_error","type":"string"},"message":{"description":"Human-readable message\nexample: slug is required","type":"string"}}}},"securityDefinitions":{"ApiKeyAuth":{"description":"User API key","type":"apiKey","name":"X-API-KEY","in":"header"},"BearerAuth":{"description":"Bearer token authentication","type":"apiKey","name":"Authorization","in":"header"},"OrgKeyAuth":{"description":"Org-level key/secret authentication","type":"apiKey","name":"X-ORG-KEY","in":"header"},"OrgSecretAuth":{"description":"Org-level secret","type":"apiKey","name":"X-ORG-SECRET","in":"header"}}}` + "schemes": {{ marshal .Schemes }},"swagger":"2.0","info":{"description":"{{escape .Description}}","title":"{{.Title}}","contact":{"name":"GlueOps"},"version":"{{.Version}}"},"host":"{{.Host}}","basePath":"{{.BasePath}}","paths":{"/.well-known/jwks.json":{"get":{"description":"Returns the JSON Web Key Set for token verification","produces":["application/json"],"tags":["Auth"],"summary":"Get JWKS","operationId":"getJWKS","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.JWKS"}}}}},"/auth/logout":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Revoke refresh token family (logout everywhere)","operationId":"Logout","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.LogoutRequest"}}],"responses":{"204":{"description":"No Content"}}}},"/auth/refresh":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Rotate refresh token","operationId":"Refresh","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.RefreshRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/callback":{"get":{"produces":["application/json"],"tags":["Auth"],"summary":"Handle social login callback","operationId":"AuthCallback","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/start":{"post":{"description":"Returns provider authorization URL for the frontend to redirect","produces":["application/json"],"tags":["Auth"],"summary":"Begin social login","operationId":"AuthStart","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.AuthStartResponse"}}}}},"/labels":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node labels for the organization in X-Org-ID. Filters: ` + "`" + `key` + "`" + `, ` + "`" + `value` + "`" + `, and ` + "`" + `q` + "`" + ` (key contains). Add ` + "`" + `include=node_pools` + "`" + ` to include linked node groups.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"List node labels (org scoped)","operationId":"ListLabels","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"Key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.LabelResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Create label (org scoped)","operationId":"CreateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Label payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateLabelRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/labels/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Get label by ID (org scoped)","operationId":"GetLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Delete label (org scoped)","operationId":"DeleteLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update label fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Update label (org scoped)","operationId":"UpdateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateLabelRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/me":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me"],"summary":"Get current user profile","operationId":"GetMe","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.meResponse"}}}},"patch":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Me"],"summary":"Update current user profile","operationId":"UpdateMe","parameters":[{"description":"Patch profile","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.updateMeRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.User"}}}}},"/me/api-keys":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"List my API keys","operationId":"ListUserAPIKeys","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"post":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"description":"Returns the plaintext key once. Store it securely on the client side.","consumes":["application/json"],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Create a new user API key","operationId":"CreateUserAPIKey","parameters":[{"description":"Key options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.createUserKeyRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"/me/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Delete a user API key","operationId":"DeleteUserAPIKey","parameters":[{"type":"string","description":"Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content"}}}},"/orgs":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List organizations I belong to","operationId":"listMyOrgs","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create organization","operationId":"createOrg","parameters":[{"description":"Org payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/models.Organization"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"409":{"description":"Conflict","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Get organization","operationId":"getOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete organization (owner)","operationId":"deleteOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"patch":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Update organization (owner/admin)","operationId":"updateOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Update payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgUpdateReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List org-scoped API keys (no secrets)","operationId":"listOrgKeys","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.APIKey"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create org key/secret pair (owner/admin)","operationId":"createOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Key name + optional expiry","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgKeyCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.orgKeyCreateResp"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys/{key_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete org key (owner/admin)","operationId":"deleteOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"Key ID (UUID)","name":"key_id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List members in org","operationId":"listMembers","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.memberOut"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Add or update a member (owner/admin)","operationId":"addOrUpdateMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"User \u0026 role","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.memberUpsertReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.memberOut"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members/{user_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Remove a member (owner/admin)","operationId":"removeMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"User ID (UUID)","name":"user_id","in":"path","required":true}],"responses":{"204":{"description":"Removed"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/servers":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns servers for the organization in X-Org-ID. Optional filters: status, role.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"List servers (org scoped)","operationId":"ListServers","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Filter by status (pending|provisioning|ready|failed)","name":"status","in":"query"},{"type":"string","description":"Filter by role","name":"role","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.ServerResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list servers","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a server bound to the org in X-Org-ID. Validates that ssh_key_id belongs to the org.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Create server (org scoped)","operationId":"CreateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Server payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateServerRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid json / missing fields / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/servers/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one server in the given organization.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Get server by ID (org scoped)","operationId":"GetServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the server.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Delete server (org scoped)","operationId":"DeleteServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update fields; changing ssh_key_id validates ownership.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Update server (org scoped)","operationId":"UpdateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateServerRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id / invalid json / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/ssh":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns ssh keys for the organization in X-Org-ID.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"List ssh keys (org scoped)","operationId":"ListPublicSshKeys","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.SshResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list keys","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Generates an RSA or ED25519 keypair, saves it, and returns metadata. For RSA you may set bits (2048/3072/4096). Default is 4096. ED25519 ignores bits.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Create ssh keypair (org scoped)","operationId":"CreateSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Key generation options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateSSHRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.SshResponse"}},"400":{"description":"invalid json / invalid bits","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"generation/create failed","schema":{"type":"string"}}}}},"/ssh/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns public key fields. Append ` + "`" + `?reveal=true` + "`" + ` to include the private key PEM.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Get ssh key by ID (org scoped)","operationId":"GetSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"type":"boolean","description":"Reveal private key PEM","name":"reveal","in":"query"}],"responses":{"200":{"description":"When reveal=true","schema":{"$ref":"#/definitions/dto.SshRevealResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes a keypair.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Delete ssh keypair (org scoped)","operationId":"DeleteSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}}},"/ssh/{id}/download":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Download ` + "`" + `part=public|private|both` + "`" + ` of the keypair. ` + "`" + `both` + "`" + ` returns a zip file.","produces":["application/json"],"tags":["Ssh"],"summary":"Download ssh key files by ID (org scoped)","operationId":"DownloadSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header","required":true},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"enum":["public","private","both"],"type":"string","description":"Which part to download","name":"part","in":"query","required":true}],"responses":{"200":{"description":"file content","schema":{"type":"string"}},"400":{"description":"invalid id / invalid part","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"download failed","schema":{"type":"string"}}}}},"/taints":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node taints for the organization in X-Org-ID. Filters: ` + "`" + `key` + "`" + `, ` + "`" + `value` + "`" + `, and ` + "`" + `q` + "`" + ` (key contains). Add ` + "`" + `include=node_pools` + "`" + ` to include linked node pools.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"List node pool taints (org scoped)","operationId":"ListTaints","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.TaintResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Create node taint (org scoped)","operationId":"CreateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Taint payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateTaintRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/taints/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Get node taint by ID (org scoped)","operationId":"GetTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Delete taint (org scoped)","operationId":"DeleteTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update taint fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Update node taint (org scoped)","operationId":"UpdateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateTaintRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}}},"definitions":{"dto.AuthStartResponse":{"type":"object","properties":{"auth_url":{"type":"string","example":"https://accounts.google.com/o/oauth2/v2/auth?client_id=..."}}},"dto.CreateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.CreateSSHRequest":{"type":"object","properties":{"bits":{"description":"Only for RSA","type":"integer"},"comment":{"type":"string","example":"deploy@autoglue"},"name":{"type":"string"},"type":{"description":"\"rsa\" (default) or \"ed25519\"","type":"string"}}},"dto.CreateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.CreateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.JWK":{"type":"object","properties":{"alg":{"type":"string","example":"RS256"},"e":{"type":"string","example":"AQAB"},"kid":{"type":"string","example":"7c6f1d0a-7a98-4e6a-9dbf-6b1af4b9f345"},"kty":{"type":"string","example":"RSA"},"n":{"type":"string"},"use":{"type":"string","example":"sig"},"x":{"type":"string"}}},"dto.JWKS":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/definitions/dto.JWK"}}}},"dto.LabelResponse":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.LogoutRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.RefreshRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.ServerResponse":{"type":"object","properties":{"created_at":{"type":"string"},"hostname":{"type":"string"},"id":{"type":"string"},"organization_id":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshRevealResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"private_key":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.TaintResponse":{"type":"object","properties":{"effect":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.TokenPair":{"type":"object","properties":{"access_token":{"type":"string","example":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ij..."},"expires_in":{"type":"integer","example":3600},"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf...."},"token_type":{"type":"string","example":"Bearer"}}},"dto.UpdateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.UpdateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.UpdateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"handlers.createUserKeyRequest":{"type":"object","properties":{"expires_in_hours":{"description":"optional TTL","type":"integer"},"name":{"type":"string"}}},"handlers.meResponse":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"emails":{"type":"array","items":{"$ref":"#/definitions/models.UserEmail"}},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"organizations":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"handlers.memberOut":{"type":"object","properties":{"email":{"type":"string"},"role":{"description":"owner/admin/member","type":"string"},"user_id":{"type":"string","format":"uuid"}}},"handlers.memberUpsertReq":{"type":"object","properties":{"role":{"type":"string","example":"member"},"user_id":{"type":"string","format":"uuid"}}},"handlers.orgCreateReq":{"type":"object","properties":{"domain":{"type":"string","example":"acme.com"},"name":{"type":"string","example":"Acme Corp"}}},"handlers.orgKeyCreateReq":{"type":"object","properties":{"expires_in_hours":{"type":"integer","example":720},"name":{"type":"string","example":"automation-bot"}}},"handlers.orgKeyCreateResp":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org_key":{"description":"shown once:","type":"string"},"org_secret":{"description":"shown once:","type":"string"},"scope":{"description":"\"org\"","type":"string"}}},"handlers.orgUpdateReq":{"type":"object","properties":{"domain":{"type":"string"},"name":{"type":"string"}}},"handlers.updateMeRequest":{"type":"object","properties":{"display_name":{"type":"string"}}},"handlers.userAPIKeyOut":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string"},"name":{"type":"string"},"plain":{"description":"Shown only on create:","type":"string"},"scope":{"description":"\"user\"","type":"string"}}},"models.APIKey":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"org_id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"revoked":{"type":"boolean"},"scope":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"user_id":{"type":"string","format":"uuid"}}},"models.Organization":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"domain":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"name":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.User":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.UserEmail":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"email":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_primary":{"type":"boolean"},"is_verified":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"},"user":{"$ref":"#/definitions/models.User"},"user_id":{"type":"string","format":"uuid"}}},"utils.ErrorResponse":{"type":"object","properties":{"code":{"description":"A machine-readable error code, e.g. \"validation_error\"\nexample: validation_error","type":"string"},"message":{"description":"Human-readable message\nexample: slug is required","type":"string"}}}},"securityDefinitions":{"ApiKeyAuth":{"description":"User API key","type":"apiKey","name":"X-API-KEY","in":"header"},"BearerAuth":{"description":"Bearer token authentication","type":"apiKey","name":"Authorization","in":"header"},"OrgKeyAuth":{"description":"Org-level key/secret authentication","type":"apiKey","name":"X-ORG-KEY","in":"header"},"OrgSecretAuth":{"description":"Org-level secret","type":"apiKey","name":"X-ORG-SECRET","in":"header"}}}` // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ diff --git a/docs/swagger.json b/docs/swagger.json index 175b5af..c8dd963 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1 +1 @@ -{"schemes":["http","https"],"swagger":"2.0","info":{"description":"API for managing K3s clusters across cloud providers","title":"AutoGlue API","contact":{"name":"GlueOps"},"version":"1.0"},"host":"localhost:8080","basePath":"/api/v1","paths":{"/.well-known/jwks.json":{"get":{"description":"Returns the JSON Web Key Set for token verification","produces":["application/json"],"tags":["Auth"],"summary":"Get JWKS","operationId":"getJWKS","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.JWKS"}}}}},"/auth/logout":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Revoke refresh token family (logout everywhere)","operationId":"Logout","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.LogoutRequest"}}],"responses":{"204":{"description":"No Content"}}}},"/auth/refresh":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Rotate refresh token","operationId":"Refresh","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.RefreshRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/callback":{"get":{"produces":["application/json"],"tags":["Auth"],"summary":"Handle social login callback","operationId":"AuthCallback","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/start":{"post":{"description":"Returns provider authorization URL for the frontend to redirect","produces":["application/json"],"tags":["Auth"],"summary":"Begin social login","operationId":"AuthStart","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.AuthStartResponse"}}}}},"/me":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me"],"summary":"Get current user profile","operationId":"GetMe","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.meResponse"}}}},"patch":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Me"],"summary":"Update current user profile","operationId":"UpdateMe","parameters":[{"description":"Patch profile","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.updateMeRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.User"}}}}},"/me/api-keys":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"List my API keys","operationId":"ListUserAPIKeys","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"post":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"description":"Returns the plaintext key once. Store it securely on the client side.","consumes":["application/json"],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Create a new user API key","operationId":"CreateUserAPIKey","parameters":[{"description":"Key options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.createUserKeyRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"/me/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Delete a user API key","operationId":"DeleteUserAPIKey","parameters":[{"type":"string","description":"Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content"}}}},"/orgs":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List organizations I belong to","operationId":"listMyOrgs","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create organization","operationId":"createOrg","parameters":[{"description":"Org payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/models.Organization"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"409":{"description":"Conflict","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Get organization","operationId":"getOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete organization (owner)","operationId":"deleteOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"patch":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Update organization (owner/admin)","operationId":"updateOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Update payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgUpdateReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List org-scoped API keys (no secrets)","operationId":"listOrgKeys","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.APIKey"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create org key/secret pair (owner/admin)","operationId":"createOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Key name + optional expiry","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgKeyCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.orgKeyCreateResp"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys/{key_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete org key (owner/admin)","operationId":"deleteOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"Key ID (UUID)","name":"key_id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List members in org","operationId":"listMembers","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.memberOut"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Add or update a member (owner/admin)","operationId":"addOrUpdateMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"User \u0026 role","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.memberUpsertReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.memberOut"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members/{user_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Remove a member (owner/admin)","operationId":"removeMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"User ID (UUID)","name":"user_id","in":"path","required":true}],"responses":{"204":{"description":"Removed"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/servers":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns servers for the organization in X-Org-ID. Optional filters: status, role.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"List servers (org scoped)","operationId":"ListServers","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Filter by status (pending|provisioning|ready|failed)","name":"status","in":"query"},{"type":"string","description":"Filter by role","name":"role","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.ServerResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list servers","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a server bound to the org in X-Org-ID. Validates that ssh_key_id belongs to the org.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Create server (org scoped)","operationId":"CreateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Server payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateServerRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid json / missing fields / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/servers/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one server in the given organization.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Get server by ID (org scoped)","operationId":"GetServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the server.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Delete server (org scoped)","operationId":"DeleteServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update fields; changing ssh_key_id validates ownership.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Update server (org scoped)","operationId":"UpdateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateServerRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id / invalid json / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/ssh":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns ssh keys for the organization in X-Org-ID.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"List ssh keys (org scoped)","operationId":"ListPublicSshKeys","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.SshResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list keys","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Generates an RSA or ED25519 keypair, saves it, and returns metadata. For RSA you may set bits (2048/3072/4096). Default is 4096. ED25519 ignores bits.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Create ssh keypair (org scoped)","operationId":"CreateSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Key generation options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateSSHRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.SshResponse"}},"400":{"description":"invalid json / invalid bits","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"generation/create failed","schema":{"type":"string"}}}}},"/ssh/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns public key fields. Append `?reveal=true` to include the private key PEM.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Get ssh key by ID (org scoped)","operationId":"GetSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"type":"boolean","description":"Reveal private key PEM","name":"reveal","in":"query"}],"responses":{"200":{"description":"When reveal=true","schema":{"$ref":"#/definitions/dto.SshRevealResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes a keypair.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Delete ssh keypair (org scoped)","operationId":"DeleteSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}}},"/ssh/{id}/download":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Download `part=public|private|both` of the keypair. `both` returns a zip file.","produces":["application/json"],"tags":["Ssh"],"summary":"Download ssh key files by ID (org scoped)","operationId":"DownloadSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header","required":true},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"enum":["public","private","both"],"type":"string","description":"Which part to download","name":"part","in":"query","required":true}],"responses":{"200":{"description":"file content","schema":{"type":"string"}},"400":{"description":"invalid id / invalid part","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"download failed","schema":{"type":"string"}}}}},"/taints":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node taints for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node pools.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"List node pool taints (org scoped)","operationId":"ListTaints","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.TaintResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Create node taint (org scoped)","operationId":"CreateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Taint payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateTaintRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/taints/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Get node taint by ID (org scoped)","operationId":"GetTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Delete taint (org scoped)","operationId":"DeleteTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update taint fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Update node taint (org scoped)","operationId":"UpdateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateTaintRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}}},"definitions":{"dto.AuthStartResponse":{"type":"object","properties":{"auth_url":{"type":"string","example":"https://accounts.google.com/o/oauth2/v2/auth?client_id=..."}}},"dto.CreateSSHRequest":{"type":"object","properties":{"bits":{"description":"Only for RSA","type":"integer"},"comment":{"type":"string","example":"deploy@autoglue"},"name":{"type":"string"},"type":{"description":"\"rsa\" (default) or \"ed25519\"","type":"string"}}},"dto.CreateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.CreateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.JWK":{"type":"object","properties":{"alg":{"type":"string","example":"RS256"},"e":{"type":"string","example":"AQAB"},"kid":{"type":"string","example":"7c6f1d0a-7a98-4e6a-9dbf-6b1af4b9f345"},"kty":{"type":"string","example":"RSA"},"n":{"type":"string"},"use":{"type":"string","example":"sig"},"x":{"type":"string"}}},"dto.JWKS":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/definitions/dto.JWK"}}}},"dto.LogoutRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.RefreshRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.ServerResponse":{"type":"object","properties":{"created_at":{"type":"string"},"hostname":{"type":"string"},"id":{"type":"string"},"organization_id":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshRevealResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"private_key":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.TaintResponse":{"type":"object","properties":{"effect":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.TokenPair":{"type":"object","properties":{"access_token":{"type":"string","example":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ij..."},"expires_in":{"type":"integer","example":3600},"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf...."},"token_type":{"type":"string","example":"Bearer"}}},"dto.UpdateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.UpdateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"handlers.createUserKeyRequest":{"type":"object","properties":{"expires_in_hours":{"description":"optional TTL","type":"integer"},"name":{"type":"string"}}},"handlers.meResponse":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"emails":{"type":"array","items":{"$ref":"#/definitions/models.UserEmail"}},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"organizations":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"handlers.memberOut":{"type":"object","properties":{"email":{"type":"string"},"role":{"description":"owner/admin/member","type":"string"},"user_id":{"type":"string","format":"uuid"}}},"handlers.memberUpsertReq":{"type":"object","properties":{"role":{"type":"string","example":"member"},"user_id":{"type":"string","format":"uuid"}}},"handlers.orgCreateReq":{"type":"object","properties":{"domain":{"type":"string","example":"acme.com"},"name":{"type":"string","example":"Acme Corp"}}},"handlers.orgKeyCreateReq":{"type":"object","properties":{"expires_in_hours":{"type":"integer","example":720},"name":{"type":"string","example":"automation-bot"}}},"handlers.orgKeyCreateResp":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org_key":{"description":"shown once:","type":"string"},"org_secret":{"description":"shown once:","type":"string"},"scope":{"description":"\"org\"","type":"string"}}},"handlers.orgUpdateReq":{"type":"object","properties":{"domain":{"type":"string"},"name":{"type":"string"}}},"handlers.updateMeRequest":{"type":"object","properties":{"display_name":{"type":"string"}}},"handlers.userAPIKeyOut":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string"},"name":{"type":"string"},"plain":{"description":"Shown only on create:","type":"string"},"scope":{"description":"\"user\"","type":"string"}}},"models.APIKey":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"org_id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"revoked":{"type":"boolean"},"scope":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"user_id":{"type":"string","format":"uuid"}}},"models.Organization":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"domain":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"name":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.User":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.UserEmail":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"email":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_primary":{"type":"boolean"},"is_verified":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"},"user":{"$ref":"#/definitions/models.User"},"user_id":{"type":"string","format":"uuid"}}},"utils.ErrorResponse":{"type":"object","properties":{"code":{"description":"A machine-readable error code, e.g. \"validation_error\"\nexample: validation_error","type":"string"},"message":{"description":"Human-readable message\nexample: slug is required","type":"string"}}}},"securityDefinitions":{"ApiKeyAuth":{"description":"User API key","type":"apiKey","name":"X-API-KEY","in":"header"},"BearerAuth":{"description":"Bearer token authentication","type":"apiKey","name":"Authorization","in":"header"},"OrgKeyAuth":{"description":"Org-level key/secret authentication","type":"apiKey","name":"X-ORG-KEY","in":"header"},"OrgSecretAuth":{"description":"Org-level secret","type":"apiKey","name":"X-ORG-SECRET","in":"header"}}} \ No newline at end of file +{"schemes":["http","https"],"swagger":"2.0","info":{"description":"API for managing K3s clusters across cloud providers","title":"AutoGlue API","contact":{"name":"GlueOps"},"version":"1.0"},"host":"localhost:8080","basePath":"/api/v1","paths":{"/.well-known/jwks.json":{"get":{"description":"Returns the JSON Web Key Set for token verification","produces":["application/json"],"tags":["Auth"],"summary":"Get JWKS","operationId":"getJWKS","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.JWKS"}}}}},"/auth/logout":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Revoke refresh token family (logout everywhere)","operationId":"Logout","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.LogoutRequest"}}],"responses":{"204":{"description":"No Content"}}}},"/auth/refresh":{"post":{"consumes":["application/json"],"produces":["application/json"],"tags":["Auth"],"summary":"Rotate refresh token","operationId":"Refresh","parameters":[{"description":"Refresh token","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.RefreshRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/callback":{"get":{"produces":["application/json"],"tags":["Auth"],"summary":"Handle social login callback","operationId":"AuthCallback","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TokenPair"}}}}},"/auth/{provider}/start":{"post":{"description":"Returns provider authorization URL for the frontend to redirect","produces":["application/json"],"tags":["Auth"],"summary":"Begin social login","operationId":"AuthStart","parameters":[{"type":"string","description":"google|github","name":"provider","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.AuthStartResponse"}}}}},"/labels":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"List node labels (org scoped)","operationId":"ListLabels","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"Key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.LabelResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Create label (org scoped)","operationId":"CreateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Label payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateLabelRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/labels/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Get label by ID (org scoped)","operationId":"GetLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the label.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Delete label (org scoped)","operationId":"DeleteLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update label fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Labels"],"summary":"Update label (org scoped)","operationId":"UpdateLabel","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Label ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateLabelRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.LabelResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/me":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me"],"summary":"Get current user profile","operationId":"GetMe","responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.meResponse"}}}},"patch":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Me"],"summary":"Update current user profile","operationId":"UpdateMe","parameters":[{"description":"Patch profile","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.updateMeRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.User"}}}}},"/me/api-keys":{"get":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"List my API keys","operationId":"ListUserAPIKeys","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"post":{"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"description":"Returns the plaintext key once. Store it securely on the client side.","consumes":["application/json"],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Create a new user API key","operationId":"CreateUserAPIKey","parameters":[{"description":"Key options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.createUserKeyRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.userAPIKeyOut"}}}}},"/me/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Me / API Keys"],"summary":"Delete a user API key","operationId":"DeleteUserAPIKey","parameters":[{"type":"string","description":"Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content"}}}},"/orgs":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List organizations I belong to","operationId":"listMyOrgs","responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create organization","operationId":"createOrg","parameters":[{"description":"Org payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/models.Organization"}},"400":{"description":"Bad Request","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"409":{"description":"Conflict","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Get organization","operationId":"getOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete organization (owner)","operationId":"deleteOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"patch":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Update organization (owner/admin)","operationId":"updateOrg","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Update payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgUpdateReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/models.Organization"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}},"404":{"description":"Not Found","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List org-scoped API keys (no secrets)","operationId":"listOrgKeys","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/models.APIKey"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Create org key/secret pair (owner/admin)","operationId":"createOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"Key name + optional expiry","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.orgKeyCreateReq"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/handlers.orgKeyCreateResp"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/api-keys/{key_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Delete org key (owner/admin)","operationId":"deleteOrgKey","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"Key ID (UUID)","name":"key_id","in":"path","required":true}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members":{"get":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"List members in org","operationId":"listMembers","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/handlers.memberOut"}}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}},"post":{"security":[{"BearerAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Orgs"],"summary":"Add or update a member (owner/admin)","operationId":"addOrUpdateMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"description":"User \u0026 role","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/handlers.memberUpsertReq"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/handlers.memberOut"}},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/orgs/{id}/members/{user_id}":{"delete":{"security":[{"BearerAuth":[]}],"produces":["application/json"],"tags":["Orgs"],"summary":"Remove a member (owner/admin)","operationId":"removeMember","parameters":[{"type":"string","description":"Org ID (UUID)","name":"id","in":"path","required":true},{"type":"string","description":"User ID (UUID)","name":"user_id","in":"path","required":true}],"responses":{"204":{"description":"Removed"},"401":{"description":"Unauthorized","schema":{"$ref":"#/definitions/utils.ErrorResponse"}}}}},"/servers":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns servers for the organization in X-Org-ID. Optional filters: status, role.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"List servers (org scoped)","operationId":"ListServers","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Filter by status (pending|provisioning|ready|failed)","name":"status","in":"query"},{"type":"string","description":"Filter by role","name":"role","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.ServerResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list servers","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a server bound to the org in X-Org-ID. Validates that ssh_key_id belongs to the org.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Create server (org scoped)","operationId":"CreateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Server payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateServerRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid json / missing fields / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/servers/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns one server in the given organization.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Get server by ID (org scoped)","operationId":"GetServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the server.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Delete server (org scoped)","operationId":"DeleteServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update fields; changing ssh_key_id validates ownership.","consumes":["application/json"],"produces":["application/json"],"tags":["Servers"],"summary":"Update server (org scoped)","operationId":"UpdateServer","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Server ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateServerRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.ServerResponse"}},"400":{"description":"invalid id / invalid json / invalid status / invalid ssh_key_id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}},"/ssh":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns ssh keys for the organization in X-Org-ID.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"List ssh keys (org scoped)","operationId":"ListPublicSshKeys","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.SshResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list keys","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Generates an RSA or ED25519 keypair, saves it, and returns metadata. For RSA you may set bits (2048/3072/4096). Default is 4096. ED25519 ignores bits.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Create ssh keypair (org scoped)","operationId":"CreateSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Key generation options","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateSSHRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.SshResponse"}},"400":{"description":"invalid json / invalid bits","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"generation/create failed","schema":{"type":"string"}}}}},"/ssh/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns public key fields. Append `?reveal=true` to include the private key PEM.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Get ssh key by ID (org scoped)","operationId":"GetSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"type":"boolean","description":"Reveal private key PEM","name":"reveal","in":"query"}],"responses":{"200":{"description":"When reveal=true","schema":{"$ref":"#/definitions/dto.SshRevealResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes a keypair.","consumes":["application/json"],"produces":["application/json"],"tags":["Ssh"],"summary":"Delete ssh keypair (org scoped)","operationId":"DeleteSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}}},"/ssh/{id}/download":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Download `part=public|private|both` of the keypair. `both` returns a zip file.","produces":["application/json"],"tags":["Ssh"],"summary":"Download ssh key files by ID (org scoped)","operationId":"DownloadSSHKey","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header","required":true},{"type":"string","description":"SSH Key ID (UUID)","name":"id","in":"path","required":true},{"enum":["public","private","both"],"type":"string","description":"Which part to download","name":"part","in":"query","required":true}],"responses":{"200":{"description":"file content","schema":{"type":"string"}},"400":{"description":"invalid id / invalid part","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"download failed","schema":{"type":"string"}}}}},"/taints":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Returns node taints for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node pools.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"List node pool taints (org scoped)","operationId":"ListTaints","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Exact key","name":"key","in":"query"},{"type":"string","description":"Exact value","name":"value","in":"query"},{"type":"string","description":"key contains (case-insensitive)","name":"q","in":"query"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/dto.TaintResponse"}}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"failed to list node taints","schema":{"type":"string"}}}},"post":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Creates a taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Create node taint (org scoped)","operationId":"CreateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"description":"Taint payload","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.CreateTaintRequest"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid json / missing fields / invalid node_pool_ids","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"create failed","schema":{"type":"string"}}}}},"/taints/{id}":{"get":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Get node taint by ID (org scoped)","operationId":"GetTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"fetch failed","schema":{"type":"string"}}}},"delete":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Permanently deletes the taint.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Delete taint (org scoped)","operationId":"DeleteTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true}],"responses":{"204":{"description":"No Content","schema":{"type":"string"}},"400":{"description":"invalid id","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"500":{"description":"delete failed","schema":{"type":"string"}}}},"patch":{"security":[{"BearerAuth":[]},{"OrgKeyAuth":[]},{"OrgSecretAuth":[]}],"description":"Partially update taint fields.","consumes":["application/json"],"produces":["application/json"],"tags":["Taints"],"summary":"Update node taint (org scoped)","operationId":"UpdateTaint","parameters":[{"type":"string","description":"Organization UUID","name":"X-Org-ID","in":"header"},{"type":"string","description":"Node Taint ID (UUID)","name":"id","in":"path","required":true},{"description":"Fields to update","name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/dto.UpdateTaintRequest"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/dto.TaintResponse"}},"400":{"description":"invalid id / invalid json","schema":{"type":"string"}},"401":{"description":"Unauthorized","schema":{"type":"string"}},"403":{"description":"organization required","schema":{"type":"string"}},"404":{"description":"not found","schema":{"type":"string"}},"500":{"description":"update failed","schema":{"type":"string"}}}}}},"definitions":{"dto.AuthStartResponse":{"type":"object","properties":{"auth_url":{"type":"string","example":"https://accounts.google.com/o/oauth2/v2/auth?client_id=..."}}},"dto.CreateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.CreateSSHRequest":{"type":"object","properties":{"bits":{"description":"Only for RSA","type":"integer"},"comment":{"type":"string","example":"deploy@autoglue"},"name":{"type":"string"},"type":{"description":"\"rsa\" (default) or \"ed25519\"","type":"string"}}},"dto.CreateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.CreateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.JWK":{"type":"object","properties":{"alg":{"type":"string","example":"RS256"},"e":{"type":"string","example":"AQAB"},"kid":{"type":"string","example":"7c6f1d0a-7a98-4e6a-9dbf-6b1af4b9f345"},"kty":{"type":"string","example":"RSA"},"n":{"type":"string"},"use":{"type":"string","example":"sig"},"x":{"type":"string"}}},"dto.JWKS":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/definitions/dto.JWK"}}}},"dto.LabelResponse":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.LogoutRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.RefreshRequest":{"type":"object","properties":{"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf..."}}},"dto.ServerResponse":{"type":"object","properties":{"created_at":{"type":"string"},"hostname":{"type":"string"},"id":{"type":"string"},"organization_id":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.SshRevealResponse":{"type":"object","properties":{"created_at":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization_id":{"type":"string"},"private_key":{"type":"string"},"public_key":{"type":"string"},"updated_at":{"type":"string"}}},"dto.TaintResponse":{"type":"object","properties":{"effect":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"dto.TokenPair":{"type":"object","properties":{"access_token":{"type":"string","example":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ij..."},"expires_in":{"type":"integer","example":3600},"refresh_token":{"type":"string","example":"m0l9o8rT3t0V8d3eFf...."},"token_type":{"type":"string","example":"Bearer"}}},"dto.UpdateLabelRequest":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"dto.UpdateServerRequest":{"type":"object","properties":{"hostname":{"type":"string"},"private_ip_address":{"type":"string"},"public_ip_address":{"type":"string"},"role":{"type":"string","example":"master|worker|bastion"},"ssh_key_id":{"type":"string"},"ssh_user":{"type":"string"},"status":{"type":"string","example":"pending|provisioning|ready|failed"}}},"dto.UpdateTaintRequest":{"type":"object","properties":{"effect":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}}},"handlers.createUserKeyRequest":{"type":"object","properties":{"expires_in_hours":{"description":"optional TTL","type":"integer"},"name":{"type":"string"}}},"handlers.meResponse":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"emails":{"type":"array","items":{"$ref":"#/definitions/models.UserEmail"}},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"organizations":{"type":"array","items":{"$ref":"#/definitions/models.Organization"}},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"handlers.memberOut":{"type":"object","properties":{"email":{"type":"string"},"role":{"description":"owner/admin/member","type":"string"},"user_id":{"type":"string","format":"uuid"}}},"handlers.memberUpsertReq":{"type":"object","properties":{"role":{"type":"string","example":"member"},"user_id":{"type":"string","format":"uuid"}}},"handlers.orgCreateReq":{"type":"object","properties":{"domain":{"type":"string","example":"acme.com"},"name":{"type":"string","example":"Acme Corp"}}},"handlers.orgKeyCreateReq":{"type":"object","properties":{"expires_in_hours":{"type":"integer","example":720},"name":{"type":"string","example":"automation-bot"}}},"handlers.orgKeyCreateResp":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org_key":{"description":"shown once:","type":"string"},"org_secret":{"description":"shown once:","type":"string"},"scope":{"description":"\"org\"","type":"string"}}},"handlers.orgUpdateReq":{"type":"object","properties":{"domain":{"type":"string"},"name":{"type":"string"}}},"handlers.updateMeRequest":{"type":"object","properties":{"display_name":{"type":"string"}}},"handlers.userAPIKeyOut":{"type":"object","properties":{"created_at":{"type":"string"},"expires_at":{"type":"string"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string"},"name":{"type":"string"},"plain":{"description":"Shown only on create:","type":"string"},"scope":{"description":"\"user\"","type":"string"}}},"models.APIKey":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"org_id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"revoked":{"type":"boolean"},"scope":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"user_id":{"type":"string","format":"uuid"}}},"models.Organization":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"domain":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"name":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.User":{"type":"object","properties":{"avatar_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_disabled":{"type":"boolean"},"primary_email":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"models.UserEmail":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"email":{"type":"string"},"id":{"description":"example: 3fa85f64-5717-4562-b3fc-2c963f66afa6","type":"string","format":"uuid"},"is_primary":{"type":"boolean"},"is_verified":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"},"user":{"$ref":"#/definitions/models.User"},"user_id":{"type":"string","format":"uuid"}}},"utils.ErrorResponse":{"type":"object","properties":{"code":{"description":"A machine-readable error code, e.g. \"validation_error\"\nexample: validation_error","type":"string"},"message":{"description":"Human-readable message\nexample: slug is required","type":"string"}}}},"securityDefinitions":{"ApiKeyAuth":{"description":"User API key","type":"apiKey","name":"X-API-KEY","in":"header"},"BearerAuth":{"description":"Bearer token authentication","type":"apiKey","name":"Authorization","in":"header"},"OrgKeyAuth":{"description":"Org-level key/secret authentication","type":"apiKey","name":"X-ORG-KEY","in":"header"},"OrgSecretAuth":{"description":"Org-level secret","type":"apiKey","name":"X-ORG-SECRET","in":"header"}}} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index ef51348..da99dfe 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -6,6 +6,13 @@ definitions: example: https://accounts.google.com/o/oauth2/v2/auth?client_id=... type: string type: object + dto.CreateLabelRequest: + properties: + key: + type: string + value: + type: string + type: object dto.CreateSSHRequest: properties: bits: @@ -77,6 +84,15 @@ definitions: $ref: '#/definitions/dto.JWK' type: array type: object + dto.LabelResponse: + properties: + id: + type: string + key: + type: string + value: + type: string + type: object dto.LogoutRequest: properties: refresh_token: @@ -176,6 +192,13 @@ definitions: example: Bearer type: string type: object + dto.UpdateLabelRequest: + properties: + key: + type: string + value: + type: string + type: object dto.UpdateServerRequest: properties: hostname: @@ -541,6 +564,255 @@ paths: summary: Rotate refresh token tags: - Auth + /labels: + get: + consumes: + - application/json + description: 'Returns node labels for the organization in X-Org-ID. Filters: + `key`, `value`, and `q` (key contains). Add `include=node_pools` to include + linked node groups.' + operationId: ListLabels + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + type: string + - description: Exact key + in: query + name: key + type: string + - description: Exact value + in: query + name: value + type: string + - description: Key contains (case-insensitive) + in: query + name: q + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/dto.LabelResponse' + type: array + "401": + description: Unauthorized + schema: + type: string + "403": + description: organization required + schema: + type: string + "500": + description: failed to list node taints + schema: + type: string + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: List node labels (org scoped) + tags: + - Labels + post: + consumes: + - application/json + description: Creates a label. + operationId: CreateLabel + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + type: string + - description: Label payload + in: body + name: body + required: true + schema: + $ref: '#/definitions/dto.CreateLabelRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/dto.LabelResponse' + "400": + description: invalid json / missing fields / invalid node_pool_ids + schema: + type: string + "401": + description: Unauthorized + schema: + type: string + "403": + description: organization required + schema: + type: string + "500": + description: create failed + schema: + type: string + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: Create label (org scoped) + tags: + - Labels + /labels/{id}: + delete: + consumes: + - application/json + description: Permanently deletes the label. + operationId: DeleteLabel + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + type: string + - description: Label ID (UUID) + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + schema: + type: string + "400": + description: invalid id + schema: + type: string + "401": + description: Unauthorized + schema: + type: string + "403": + description: organization required + schema: + type: string + "500": + description: delete failed + schema: + type: string + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: Delete label (org scoped) + tags: + - Labels + get: + consumes: + - application/json + description: Returns one label. + operationId: GetLabel + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + type: string + - description: Label ID (UUID) + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.LabelResponse' + "400": + description: invalid id + schema: + type: string + "401": + description: Unauthorized + schema: + type: string + "403": + description: organization required + schema: + type: string + "404": + description: not found + schema: + type: string + "500": + description: fetch failed + schema: + type: string + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: Get label by ID (org scoped) + tags: + - Labels + patch: + consumes: + - application/json + description: Partially update label fields. + operationId: UpdateLabel + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + type: string + - description: Label ID (UUID) + in: path + name: id + required: true + type: string + - description: Fields to update + in: body + name: body + required: true + schema: + $ref: '#/definitions/dto.UpdateLabelRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dto.LabelResponse' + "400": + description: invalid id / invalid json + schema: + type: string + "401": + description: Unauthorized + schema: + type: string + "403": + description: organization required + schema: + type: string + "404": + description: not found + schema: + type: string + "500": + description: update failed + schema: + type: string + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: Update label (org scoped) + tags: + - Labels /me: get: operationId: GetMe diff --git a/internal/api/routes.go b/internal/api/routes.go index af0066b..07890f7 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -136,6 +136,15 @@ func NewRouter(db *gorm.DB) http.Handler { s.Patch("/{id}", handlers.UpdateTaint(db)) s.Delete("/{id}", handlers.DeleteTaint(db)) }) + + v1.Route("/labels", func(s chi.Router) { + s.Use(authOrg) + s.Get("/", handlers.ListLabels(db)) + s.Post("/", handlers.CreateLabel(db)) + s.Get("/{id}", handlers.GetLabel(db)) + s.Patch("/{id}", handlers.UpdateLabel(db)) + s.Delete("/{id}", handlers.DeleteLabel(db)) + }) }) }) if config.IsDebug() { diff --git a/internal/app/runtime.go b/internal/app/runtime.go index 5665184..8c66f0b 100644 --- a/internal/app/runtime.go +++ b/internal/app/runtime.go @@ -35,6 +35,7 @@ func NewRuntime() *Runtime { &models.SshKey{}, &models.Server{}, &models.Taint{}, + &models.Label{}, ) if err != nil { log.Fatalf("Error initializing database: %v", err) diff --git a/internal/handlers/dto/labels.go b/internal/handlers/dto/labels.go new file mode 100644 index 0000000..9a5e255 --- /dev/null +++ b/internal/handlers/dto/labels.go @@ -0,0 +1,19 @@ +package dto + +import "github.com/google/uuid" + +type LabelResponse struct { + ID uuid.UUID `json:"id"` + Key string `json:"key"` + Value string `json:"value"` +} + +type CreateLabelRequest struct { + Key string `json:"key"` + Value string `json:"value"` +} + +type UpdateLabelRequest struct { + Key *string `json:"key"` + Value *string `json:"value"` +} diff --git a/internal/handlers/labels.go b/internal/handlers/labels.go new file mode 100644 index 0000000..cf4d8d3 --- /dev/null +++ b/internal/handlers/labels.go @@ -0,0 +1,290 @@ +package handlers + +import ( + "encoding/json" + "errors" + "net/http" + "strings" + + "github.com/glueops/autoglue/internal/api/httpmiddleware" + "github.com/glueops/autoglue/internal/handlers/dto" + "github.com/glueops/autoglue/internal/models" + "github.com/glueops/autoglue/internal/utils" + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "gorm.io/gorm" +) + +// ListLabels godoc +// @ID ListLabels +// @Summary List node labels (org scoped) +// @Description Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups. +// @Tags Labels +// @Accept json +// @Produce json +// @Param X-Org-ID header string false "Organization UUID" +// @Param key query string false "Exact key" +// @Param value query string false "Exact value" +// @Param q query string false "Key contains (case-insensitive)" +// @Success 200 {array} dto.LabelResponse +// @Failure 401 {string} string "Unauthorized" +// @Failure 403 {string} string "organization required" +// @Failure 500 {string} string "failed to list node taints" +// @Router /labels [get] +// @Security BearerAuth +// @Security OrgKeyAuth +// @Security OrgSecretAuth +func ListLabels(db *gorm.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + orgID, ok := httpmiddleware.OrgIDFrom(r.Context()) + if !ok { + utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID") + return + } + + q := db.Where("organization_id = ?", orgID) + + if key := strings.TrimSpace(r.URL.Query().Get("key")); key != "" { + q = q.Where(`key = ?`, key) + } + if val := strings.TrimSpace(r.URL.Query().Get("value")); val != "" { + q = q.Where(`value = ?`, val) + } + if needle := strings.TrimSpace(r.URL.Query().Get("q")); needle != "" { + q = q.Where(`key ILIKE ?`, "%"+needle+"%") + } + var rows []models.Label + if err := q.Order("created_at DESC").Find(&rows).Error; err != nil { + utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error") + return + } + + out := make([]dto.LabelResponse, 0, len(rows)) + for _, row := range rows { + out = append(out, dto.LabelResponse{ + Key: row.Key, + Value: row.Value, + ID: row.ID, + }) + } + utils.WriteJSON(w, http.StatusOK, out) + } +} + +// GetLabel godoc +// @ID GetLabel +// @Summary Get label by ID (org scoped) +// @Description Returns one label. +// @Tags Labels +// @Accept json +// @Produce json +// @Param X-Org-ID header string false "Organization UUID" +// @Param id path string true "Label ID (UUID)" +// @Success 200 {object} dto.LabelResponse +// @Failure 400 {string} string "invalid id" +// @Failure 401 {string} string "Unauthorized" +// @Failure 403 {string} string "organization required" +// @Failure 404 {string} string "not found" +// @Failure 500 {string} string "fetch failed" +// @Router /labels/{id} [get] +// @Security BearerAuth +// @Security OrgKeyAuth +// @Security OrgSecretAuth +func GetLabel(db *gorm.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + orgID, ok := httpmiddleware.OrgIDFrom(r.Context()) + if !ok { + utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID") + return + } + + id, err := uuid.Parse(chi.URLParam(r, "id")) + if err != nil { + utils.WriteError(w, http.StatusBadRequest, "id_required", "id required") + return + } + + var row models.Label + if err := db.Where("id = ? AND organization_id = ?", id, orgID).First(&row).Error; err != nil { + utils.WriteError(w, http.StatusNotFound, "label_not_found", "label not found") + return + } + + out := dto.LabelResponse{ + Key: row.Key, + Value: row.Value, + ID: row.ID, + } + utils.WriteJSON(w, http.StatusOK, out) + } +} + +// CreateLabel godoc +// @ID CreateLabel +// @Summary Create label (org scoped) +// @Description Creates a label. +// @Tags Labels +// @Accept json +// @Produce json +// @Param X-Org-ID header string false "Organization UUID" +// @Param body body dto.CreateLabelRequest true "Label payload" +// @Success 201 {object} dto.LabelResponse +// @Failure 400 {string} string "invalid json / missing fields / invalid node_pool_ids" +// @Failure 401 {string} string "Unauthorized" +// @Failure 403 {string} string "organization required" +// @Failure 500 {string} string "create failed" +// @Router /labels [post] +// @Security BearerAuth +// @Security OrgKeyAuth +// @Security OrgSecretAuth +func CreateLabel(db *gorm.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + orgID, ok := httpmiddleware.OrgIDFrom(r.Context()) + if !ok { + utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID") + return + } + + var req dto.CreateLabelRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + utils.WriteError(w, http.StatusBadRequest, "bad_request", "bad request") + return + } + + req.Key = strings.TrimSpace(req.Key) + req.Value = strings.TrimSpace(req.Value) + + if req.Key == "" || req.Value == "" { + utils.WriteError(w, http.StatusBadRequest, "bad_request", "missing key/value") + return + } + + l := models.Label{ + OrganizationID: orgID, + Key: req.Key, + Value: req.Value, + } + if err := db.Create(&l).Error; err != nil { + utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error") + return + } + + out := dto.LabelResponse{ + ID: l.ID, + Key: l.Key, + Value: l.Value, + } + utils.WriteJSON(w, http.StatusCreated, out) + } +} + +// UpdateLabel godoc +// @ID UpdateLabel +// @Summary Update label (org scoped) +// @Description Partially update label fields. +// @Tags Labels +// @Accept json +// @Produce json +// @Param X-Org-ID header string false "Organization UUID" +// @Param id path string true "Label ID (UUID)" +// @Param body body dto.UpdateLabelRequest true "Fields to update" +// @Success 200 {object} dto.LabelResponse +// @Failure 400 {string} string "invalid id / invalid json" +// @Failure 401 {string} string "Unauthorized" +// @Failure 403 {string} string "organization required" +// @Failure 404 {string} string "not found" +// @Failure 500 {string} string "update failed" +// @Router /labels/{id} [patch] +// @Security BearerAuth +// @Security OrgKeyAuth +// @Security OrgSecretAuth +func UpdateLabel(db *gorm.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + orgID, ok := httpmiddleware.OrgIDFrom(r.Context()) + if !ok { + utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID") + return + } + + id, err := uuid.Parse(chi.URLParam(r, "id")) + if err != nil { + utils.WriteError(w, http.StatusBadRequest, "id_required", "id required") + return + } + + var l models.Label + if err := db.Where("id = ? AND organization_id = ?", id, orgID).First(&l).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + utils.WriteError(w, http.StatusNotFound, "label_not_found", "label not found") + return + } + utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error") + return + } + + var req dto.UpdateLabelRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + utils.WriteError(w, http.StatusBadRequest, "bad_request", "bad request") + return + } + + next := l + if req.Key != nil { + next.Key = strings.TrimSpace(*req.Key) + } + if req.Value != nil { + next.Value = strings.TrimSpace(*req.Value) + } + + if err := db.Save(&next).Error; err != nil { + utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error") + return + } + out := dto.LabelResponse{ + ID: next.ID, + Key: next.Key, + Value: next.Value, + } + utils.WriteJSON(w, http.StatusOK, out) + } +} + +// DeleteLabel godoc +// @ID DeleteLabel +// @Summary Delete label (org scoped) +// @Description Permanently deletes the label. +// @Tags Labels +// @Accept json +// @Produce json +// @Param X-Org-ID header string false "Organization UUID" +// @Param id path string true "Label ID (UUID)" +// @Success 204 {string} string "No Content" +// @Failure 400 {string} string "invalid id" +// @Failure 401 {string} string "Unauthorized" +// @Failure 403 {string} string "organization required" +// @Failure 500 {string} string "delete failed" +// @Router /labels/{id} [delete] +// @Security BearerAuth +// @Security OrgKeyAuth +// @Security OrgSecretAuth +func DeleteLabel(db *gorm.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + orgID, ok := httpmiddleware.OrgIDFrom(r.Context()) + if !ok { + utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID") + return + } + + id, err := uuid.Parse(chi.URLParam(r, "id")) + if err != nil { + utils.WriteError(w, http.StatusBadRequest, "id_required", "id required") + return + } + + if err := db.Where("id = ? AND organization_id = ?", id, orgID).Delete(&models.Label{}).Error; err != nil { + utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error") + return + } + w.WriteHeader(http.StatusNoContent) + } +} diff --git a/internal/handlers/ssh.go b/internal/handlers/ssh.go index 2d590ca..5dbef35 100644 --- a/internal/handlers/ssh.go +++ b/internal/handlers/ssh.go @@ -363,6 +363,7 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc { } if mode == "json" { + prefix := keyFilenamePrefix(key.PublicKey) resp := dto.SshMaterialJSON{ ID: key.ID.String(), Name: key.Name, @@ -372,7 +373,7 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc { case "public": pub := key.PublicKey resp.PublicKey = &pub - resp.Filenames = []string{fmt.Sprintf("id_rsa_%s.pub", key.ID.String())} + resp.Filenames = []string{fmt.Sprintf("%s_%s.pub", prefix, key.ID.String())} utils.WriteJSON(w, http.StatusOK, resp) return @@ -383,7 +384,7 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc { return } resp.PrivatePEM = &plain - resp.Filenames = []string{fmt.Sprintf("id_rsa_%s.pem", key.ID.String())} + resp.Filenames = []string{fmt.Sprintf("%s_%s.pem", prefix, key.ID.String())} utils.WriteJSON(w, http.StatusOK, resp) return @@ -396,16 +397,16 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc { var buf bytes.Buffer zw := zip.NewWriter(&buf) - _ = toZipFile(fmt.Sprintf("id_rsa_%s.pem", key.ID.String()), []byte(plain), zw) - _ = toZipFile(fmt.Sprintf("id_rsa_%s.pub", key.ID.String()), []byte(key.PublicKey), zw) + _ = toZipFile(fmt.Sprintf("%s_%s.pem", prefix, key.ID.String()), []byte(plain), zw) + _ = toZipFile(fmt.Sprintf("%s_%s.pub", prefix, key.ID.String()), []byte(key.PublicKey), zw) _ = zw.Close() b64 := utils.EncodeB64(buf.Bytes()) resp.ZipBase64 = &b64 resp.Filenames = []string{ - fmt.Sprintf("id_rsa_%s.zip", key.ID.String()), - fmt.Sprintf("id_rsa_%s.pem", key.ID.String()), - fmt.Sprintf("id_rsa_%s.pub", key.ID.String()), + fmt.Sprintf("%s_%s.zip", prefix, key.ID.String()), + fmt.Sprintf("%s_%s.pem", prefix, key.ID.String()), + fmt.Sprintf("%s_%s.pub", prefix, key.ID.String()), } utils.WriteJSON(w, http.StatusOK, resp) return @@ -512,12 +513,18 @@ func toZipFile(filename string, content []byte, zw *zip.Writer) error { } func keyFilenamePrefix(pubAuth string) string { - // OpenSSH authorized keys start with the algorithm name - if strings.HasPrefix(pubAuth, "ssh-ed25519 ") { - return "id_ed25519" + pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubAuth)) + if err != nil { + return "id_key" + } + switch pk.Type() { + case "ssh-ed25519": + return "id_ed25519" + case "ssh-rsa": + return "id_rsa" + default: + return "id_key" } - // default to RSA - return "id_rsa" } func GenerateEd25519PEMAndAuthorized(comment string) (privPEM string, authorized string, err error) { diff --git a/internal/models/label.go b/internal/models/label.go new file mode 100644 index 0000000..7731f9d --- /dev/null +++ b/internal/models/label.go @@ -0,0 +1,18 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +type Label struct { + ID uuid.UUID `gorm:"type:uuid;default:gen_random_uuid();primaryKey" json:"id"` + OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id"` + Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"` + Key string `gorm:"not null" json:"key"` + Value string `gorm:"not null" json:"value"` + NodePools []NodePool `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"servers,omitempty"` + CreatedAt time.Time `gorm:"column:created_at;not null;default:now()" json:"created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at;not null;default:now()" json:"updated_at"` +} diff --git a/internal/models/node_pool.go b/internal/models/node_pool.go index bd5a29d..4663522 100644 --- a/internal/models/node_pool.go +++ b/internal/models/node_pool.go @@ -13,9 +13,11 @@ type NodePool struct { Name string `gorm:"not null" json:"name"` Servers []Server `gorm:"many2many:node_servers;constraint:OnDelete:CASCADE" json:"servers,omitempty"` //Annotations []Annotation `gorm:"many2many:node_annotations;constraint:OnDelete:CASCADE" json:"annotations,omitempty"` - //Labels []Label `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"labels,omitempty"` + Labels []Label `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"labels,omitempty"` Taints []Taint `gorm:"many2many:node_taints;constraint:OnDelete:CASCADE" json:"taints,omitempty"` //Clusters []Cluster `gorm:"many2many:cluster_node_pools;constraint:OnDelete:CASCADE" json:"clusters,omitempty"` + Topology string `gorm:"not null" json:"topology,omitempty"` // stacked or external + Role string `gorm:"not null" json:"role,omitempty"` // master, worker, ort etcd (etcd only if topology = external CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at" format:"date-time"` UpdatedAt time.Time `gorm:"not null;default:now()" json:"updated_at" format:"date-time"` } diff --git a/sdk/go/.openapi-generator/FILES b/sdk/go/.openapi-generator/FILES index ea6b4b5..6bc0498 100644 --- a/sdk/go/.openapi-generator/FILES +++ b/sdk/go/.openapi-generator/FILES @@ -3,6 +3,7 @@ README.md api/openapi.yaml api_auth.go +api_labels.go api_me.go api_me_api_keys.go api_orgs.go @@ -13,11 +14,13 @@ client.go configuration.go docs/AuthAPI.md docs/DtoAuthStartResponse.md +docs/DtoCreateLabelRequest.md docs/DtoCreateSSHRequest.md docs/DtoCreateServerRequest.md docs/DtoCreateTaintRequest.md docs/DtoJWK.md docs/DtoJWKS.md +docs/DtoLabelResponse.md docs/DtoLogoutRequest.md docs/DtoRefreshRequest.md docs/DtoServerResponse.md @@ -25,6 +28,7 @@ docs/DtoSshResponse.md docs/DtoSshRevealResponse.md docs/DtoTaintResponse.md docs/DtoTokenPair.md +docs/DtoUpdateLabelRequest.md docs/DtoUpdateServerRequest.md docs/DtoUpdateTaintRequest.md docs/HandlersCreateUserKeyRequest.md @@ -37,6 +41,7 @@ docs/HandlersOrgKeyCreateResp.md docs/HandlersOrgUpdateReq.md docs/HandlersUpdateMeRequest.md docs/HandlersUserAPIKeyOut.md +docs/LabelsAPI.md docs/MeAPI.md docs/MeAPIKeysAPI.md docs/ModelsAPIKey.md @@ -52,11 +57,13 @@ git_push.sh go.mod go.sum model_dto_auth_start_response.go +model_dto_create_label_request.go model_dto_create_server_request.go model_dto_create_ssh_request.go model_dto_create_taint_request.go model_dto_jwk.go model_dto_jwks.go +model_dto_label_response.go model_dto_logout_request.go model_dto_refresh_request.go model_dto_server_response.go @@ -64,6 +71,7 @@ model_dto_ssh_response.go model_dto_ssh_reveal_response.go model_dto_taint_response.go model_dto_token_pair.go +model_dto_update_label_request.go model_dto_update_server_request.go model_dto_update_taint_request.go model_handlers_create_user_key_request.go @@ -82,5 +90,4 @@ model_models_user.go model_models_user_email.go model_utils_error_response.go response.go -test/api_taints_test.go utils.go diff --git a/sdk/go/README.md b/sdk/go/README.md index 5b00725..747ce3d 100644 --- a/sdk/go/README.md +++ b/sdk/go/README.md @@ -83,6 +83,11 @@ Class | Method | HTTP request | Description *AuthAPI* | [**GetJWKS**](docs/AuthAPI.md#getjwks) | **Get** /.well-known/jwks.json | Get JWKS *AuthAPI* | [**Logout**](docs/AuthAPI.md#logout) | **Post** /auth/logout | Revoke refresh token family (logout everywhere) *AuthAPI* | [**Refresh**](docs/AuthAPI.md#refresh) | **Post** /auth/refresh | Rotate refresh token +*LabelsAPI* | [**CreateLabel**](docs/LabelsAPI.md#createlabel) | **Post** /labels | Create label (org scoped) +*LabelsAPI* | [**DeleteLabel**](docs/LabelsAPI.md#deletelabel) | **Delete** /labels/{id} | Delete label (org scoped) +*LabelsAPI* | [**GetLabel**](docs/LabelsAPI.md#getlabel) | **Get** /labels/{id} | Get label by ID (org scoped) +*LabelsAPI* | [**ListLabels**](docs/LabelsAPI.md#listlabels) | **Get** /labels | List node labels (org scoped) +*LabelsAPI* | [**UpdateLabel**](docs/LabelsAPI.md#updatelabel) | **Patch** /labels/{id} | Update label (org scoped) *MeAPI* | [**GetMe**](docs/MeAPI.md#getme) | **Get** /me | Get current user profile *MeAPI* | [**UpdateMe**](docs/MeAPI.md#updateme) | **Patch** /me | Update current user profile *MeAPIKeysAPI* | [**CreateUserAPIKey**](docs/MeAPIKeysAPI.md#createuserapikey) | **Post** /me/api-keys | Create a new user API key @@ -119,11 +124,13 @@ Class | Method | HTTP request | Description ## Documentation For Models - [DtoAuthStartResponse](docs/DtoAuthStartResponse.md) + - [DtoCreateLabelRequest](docs/DtoCreateLabelRequest.md) - [DtoCreateSSHRequest](docs/DtoCreateSSHRequest.md) - [DtoCreateServerRequest](docs/DtoCreateServerRequest.md) - [DtoCreateTaintRequest](docs/DtoCreateTaintRequest.md) - [DtoJWK](docs/DtoJWK.md) - [DtoJWKS](docs/DtoJWKS.md) + - [DtoLabelResponse](docs/DtoLabelResponse.md) - [DtoLogoutRequest](docs/DtoLogoutRequest.md) - [DtoRefreshRequest](docs/DtoRefreshRequest.md) - [DtoServerResponse](docs/DtoServerResponse.md) @@ -131,6 +138,7 @@ Class | Method | HTTP request | Description - [DtoSshRevealResponse](docs/DtoSshRevealResponse.md) - [DtoTaintResponse](docs/DtoTaintResponse.md) - [DtoTokenPair](docs/DtoTokenPair.md) + - [DtoUpdateLabelRequest](docs/DtoUpdateLabelRequest.md) - [DtoUpdateServerRequest](docs/DtoUpdateServerRequest.md) - [DtoUpdateTaintRequest](docs/DtoUpdateTaintRequest.md) - [HandlersCreateUserKeyRequest](docs/HandlersCreateUserKeyRequest.md) diff --git a/sdk/go/api/openapi.yaml b/sdk/go/api/openapi.yaml index f17ac27..01cb352 100644 --- a/sdk/go/api/openapi.yaml +++ b/sdk/go/api/openapi.yaml @@ -103,6 +103,302 @@ paths: summary: Begin social login tags: - Auth + /labels: + get: + description: "Returns node labels for the organization in X-Org-ID. Filters:\ + \ `key`, `value`, and `q` (key contains). Add `include=node_pools` to include\ + \ linked node groups." + operationId: ListLabels + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + schema: + type: string + - description: Exact key + in: query + name: key + schema: + type: string + - description: Exact value + in: query + name: value + schema: + type: string + - description: Key contains (case-insensitive) + in: query + name: q + schema: + type: string + responses: + "200": + content: + application/json: + schema: + items: + $ref: "#/components/schemas/dto.LabelResponse" + type: array + description: OK + "401": + content: + application/json: + schema: + type: string + description: Unauthorized + "403": + content: + application/json: + schema: + type: string + description: organization required + "500": + content: + application/json: + schema: + type: string + description: failed to list node taints + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: List node labels (org scoped) + tags: + - Labels + post: + description: Creates a label. + operationId: CreateLabel + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/dto.CreateLabelRequest" + description: Label payload + required: true + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/dto.LabelResponse" + description: Created + "400": + content: + application/json: + schema: + type: string + description: invalid json / missing fields / invalid node_pool_ids + "401": + content: + application/json: + schema: + type: string + description: Unauthorized + "403": + content: + application/json: + schema: + type: string + description: organization required + "500": + content: + application/json: + schema: + type: string + description: create failed + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: Create label (org scoped) + tags: + - Labels + x-codegen-request-body-name: body + /labels/{id}: + delete: + description: Permanently deletes the label. + operationId: DeleteLabel + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + schema: + type: string + - description: Label ID (UUID) + in: path + name: id + required: true + schema: + type: string + responses: + "204": + content: + application/json: + schema: + type: string + description: No Content + "400": + content: + application/json: + schema: + type: string + description: invalid id + "401": + content: + application/json: + schema: + type: string + description: Unauthorized + "403": + content: + application/json: + schema: + type: string + description: organization required + "500": + content: + application/json: + schema: + type: string + description: delete failed + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: Delete label (org scoped) + tags: + - Labels + get: + description: Returns one label. + operationId: GetLabel + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + schema: + type: string + - description: Label ID (UUID) + in: path + name: id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/dto.LabelResponse" + description: OK + "400": + content: + application/json: + schema: + type: string + description: invalid id + "401": + content: + application/json: + schema: + type: string + description: Unauthorized + "403": + content: + application/json: + schema: + type: string + description: organization required + "404": + content: + application/json: + schema: + type: string + description: not found + "500": + content: + application/json: + schema: + type: string + description: fetch failed + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: Get label by ID (org scoped) + tags: + - Labels + patch: + description: Partially update label fields. + operationId: UpdateLabel + parameters: + - description: Organization UUID + in: header + name: X-Org-ID + schema: + type: string + - description: Label ID (UUID) + in: path + name: id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/dto.UpdateLabelRequest" + description: Fields to update + required: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/dto.LabelResponse" + description: OK + "400": + content: + application/json: + schema: + type: string + description: invalid id / invalid json + "401": + content: + application/json: + schema: + type: string + description: Unauthorized + "403": + content: + application/json: + schema: + type: string + description: organization required + "404": + content: + application/json: + schema: + type: string + description: not found + "500": + content: + application/json: + schema: + type: string + description: update failed + security: + - BearerAuth: [] + - OrgKeyAuth: [] + - OrgSecretAuth: [] + summary: Update label (org scoped) + tags: + - Labels + x-codegen-request-body-name: body /me: get: operationId: GetMe @@ -1452,6 +1748,13 @@ components: example: https://accounts.google.com/o/oauth2/v2/auth?client_id=... type: string type: object + dto.CreateLabelRequest: + properties: + key: + type: string + value: + type: string + type: object dto.CreateSSHRequest: properties: bits: @@ -1547,6 +1850,19 @@ components: $ref: "#/components/schemas/dto.JWK" type: array type: object + dto.LabelResponse: + example: + id: id + value: value + key: key + properties: + id: + type: string + key: + type: string + value: + type: string + type: object dto.LogoutRequest: properties: refresh_token: @@ -1685,6 +2001,13 @@ components: example: Bearer type: string type: object + dto.UpdateLabelRequest: + properties: + key: + type: string + value: + type: string + type: object dto.UpdateServerRequest: properties: hostname: diff --git a/sdk/go/api_labels.go b/sdk/go/api_labels.go new file mode 100644 index 0000000..bd2de5d --- /dev/null +++ b/sdk/go/api_labels.go @@ -0,0 +1,1075 @@ +/* +AutoGlue API + +API for managing K3s clusters across cloud providers + +API version: 1.0 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package autoglue + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" + "strings" +) + +// LabelsAPIService LabelsAPI service +type LabelsAPIService service + +type ApiCreateLabelRequest struct { + ctx context.Context + ApiService *LabelsAPIService + body *DtoCreateLabelRequest + xOrgID *string +} + +// Label payload +func (r ApiCreateLabelRequest) Body(body DtoCreateLabelRequest) ApiCreateLabelRequest { + r.body = &body + return r +} + +// Organization UUID +func (r ApiCreateLabelRequest) XOrgID(xOrgID string) ApiCreateLabelRequest { + r.xOrgID = &xOrgID + return r +} + +func (r ApiCreateLabelRequest) Execute() (*DtoLabelResponse, *http.Response, error) { + return r.ApiService.CreateLabelExecute(r) +} + +/* +CreateLabel Create label (org scoped) + +Creates a label. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiCreateLabelRequest +*/ +func (a *LabelsAPIService) CreateLabel(ctx context.Context) ApiCreateLabelRequest { + return ApiCreateLabelRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// +// @return DtoLabelResponse +func (a *LabelsAPIService) CreateLabelExecute(r ApiCreateLabelRequest) (*DtoLabelResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *DtoLabelResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "LabelsAPIService.CreateLabel") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/labels" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.body == nil { + return localVarReturnValue, nil, reportError("body is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xOrgID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-Org-ID", r.xOrgID, "", "") + } + // body params + localVarPostBody = r.body + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgKeyAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-KEY"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgSecretAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-SECRET"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["BearerAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["Authorization"] = key + } + } + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 403 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiDeleteLabelRequest struct { + ctx context.Context + ApiService *LabelsAPIService + id string + xOrgID *string +} + +// Organization UUID +func (r ApiDeleteLabelRequest) XOrgID(xOrgID string) ApiDeleteLabelRequest { + r.xOrgID = &xOrgID + return r +} + +func (r ApiDeleteLabelRequest) Execute() (string, *http.Response, error) { + return r.ApiService.DeleteLabelExecute(r) +} + +/* +DeleteLabel Delete label (org scoped) + +Permanently deletes the label. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param id Label ID (UUID) + @return ApiDeleteLabelRequest +*/ +func (a *LabelsAPIService) DeleteLabel(ctx context.Context, id string) ApiDeleteLabelRequest { + return ApiDeleteLabelRequest{ + ApiService: a, + ctx: ctx, + id: id, + } +} + +// Execute executes the request +// +// @return string +func (a *LabelsAPIService) DeleteLabelExecute(r ApiDeleteLabelRequest) (string, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodDelete + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue string + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "LabelsAPIService.DeleteLabel") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/labels/{id}" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xOrgID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-Org-ID", r.xOrgID, "", "") + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgKeyAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-KEY"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgSecretAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-SECRET"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["BearerAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["Authorization"] = key + } + } + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 403 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiGetLabelRequest struct { + ctx context.Context + ApiService *LabelsAPIService + id string + xOrgID *string +} + +// Organization UUID +func (r ApiGetLabelRequest) XOrgID(xOrgID string) ApiGetLabelRequest { + r.xOrgID = &xOrgID + return r +} + +func (r ApiGetLabelRequest) Execute() (*DtoLabelResponse, *http.Response, error) { + return r.ApiService.GetLabelExecute(r) +} + +/* +GetLabel Get label by ID (org scoped) + +Returns one label. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param id Label ID (UUID) + @return ApiGetLabelRequest +*/ +func (a *LabelsAPIService) GetLabel(ctx context.Context, id string) ApiGetLabelRequest { + return ApiGetLabelRequest{ + ApiService: a, + ctx: ctx, + id: id, + } +} + +// Execute executes the request +// +// @return DtoLabelResponse +func (a *LabelsAPIService) GetLabelExecute(r ApiGetLabelRequest) (*DtoLabelResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *DtoLabelResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "LabelsAPIService.GetLabel") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/labels/{id}" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xOrgID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-Org-ID", r.xOrgID, "", "") + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgKeyAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-KEY"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgSecretAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-SECRET"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["BearerAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["Authorization"] = key + } + } + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 403 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiListLabelsRequest struct { + ctx context.Context + ApiService *LabelsAPIService + xOrgID *string + key *string + value *string + q *string +} + +// Organization UUID +func (r ApiListLabelsRequest) XOrgID(xOrgID string) ApiListLabelsRequest { + r.xOrgID = &xOrgID + return r +} + +// Exact key +func (r ApiListLabelsRequest) Key(key string) ApiListLabelsRequest { + r.key = &key + return r +} + +// Exact value +func (r ApiListLabelsRequest) Value(value string) ApiListLabelsRequest { + r.value = &value + return r +} + +// Key contains (case-insensitive) +func (r ApiListLabelsRequest) Q(q string) ApiListLabelsRequest { + r.q = &q + return r +} + +func (r ApiListLabelsRequest) Execute() ([]DtoLabelResponse, *http.Response, error) { + return r.ApiService.ListLabelsExecute(r) +} + +/* +ListLabels List node labels (org scoped) + +Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiListLabelsRequest +*/ +func (a *LabelsAPIService) ListLabels(ctx context.Context) ApiListLabelsRequest { + return ApiListLabelsRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// +// @return []DtoLabelResponse +func (a *LabelsAPIService) ListLabelsExecute(r ApiListLabelsRequest) ([]DtoLabelResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []DtoLabelResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "LabelsAPIService.ListLabels") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/labels" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.key != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "key", r.key, "", "") + } + if r.value != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "value", r.value, "", "") + } + if r.q != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "q", r.q, "", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xOrgID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-Org-ID", r.xOrgID, "", "") + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgKeyAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-KEY"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgSecretAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-SECRET"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["BearerAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["Authorization"] = key + } + } + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 403 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiUpdateLabelRequest struct { + ctx context.Context + ApiService *LabelsAPIService + id string + body *DtoUpdateLabelRequest + xOrgID *string +} + +// Fields to update +func (r ApiUpdateLabelRequest) Body(body DtoUpdateLabelRequest) ApiUpdateLabelRequest { + r.body = &body + return r +} + +// Organization UUID +func (r ApiUpdateLabelRequest) XOrgID(xOrgID string) ApiUpdateLabelRequest { + r.xOrgID = &xOrgID + return r +} + +func (r ApiUpdateLabelRequest) Execute() (*DtoLabelResponse, *http.Response, error) { + return r.ApiService.UpdateLabelExecute(r) +} + +/* +UpdateLabel Update label (org scoped) + +Partially update label fields. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param id Label ID (UUID) + @return ApiUpdateLabelRequest +*/ +func (a *LabelsAPIService) UpdateLabel(ctx context.Context, id string) ApiUpdateLabelRequest { + return ApiUpdateLabelRequest{ + ApiService: a, + ctx: ctx, + id: id, + } +} + +// Execute executes the request +// +// @return DtoLabelResponse +func (a *LabelsAPIService) UpdateLabelExecute(r ApiUpdateLabelRequest) (*DtoLabelResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPatch + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *DtoLabelResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "LabelsAPIService.UpdateLabel") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/labels/{id}" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.body == nil { + return localVarReturnValue, nil, reportError("body is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xOrgID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-Org-ID", r.xOrgID, "", "") + } + // body params + localVarPostBody = r.body + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgKeyAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-KEY"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["OrgSecretAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-ORG-SECRET"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["BearerAuth"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["Authorization"] = key + } + } + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 403 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v string + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/sdk/go/client.go b/sdk/go/client.go index dc0c0c6..f21fafb 100644 --- a/sdk/go/client.go +++ b/sdk/go/client.go @@ -50,6 +50,8 @@ type APIClient struct { AuthAPI *AuthAPIService + LabelsAPI *LabelsAPIService + MeAPI *MeAPIService MeAPIKeysAPI *MeAPIKeysAPIService @@ -80,6 +82,7 @@ func NewAPIClient(cfg *Configuration) *APIClient { // API Services c.AuthAPI = (*AuthAPIService)(&c.common) + c.LabelsAPI = (*LabelsAPIService)(&c.common) c.MeAPI = (*MeAPIService)(&c.common) c.MeAPIKeysAPI = (*MeAPIKeysAPIService)(&c.common) c.OrgsAPI = (*OrgsAPIService)(&c.common) diff --git a/sdk/go/docs/DtoCreateLabelRequest.md b/sdk/go/docs/DtoCreateLabelRequest.md new file mode 100644 index 0000000..a0469fc --- /dev/null +++ b/sdk/go/docs/DtoCreateLabelRequest.md @@ -0,0 +1,82 @@ +# DtoCreateLabelRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Key** | Pointer to **string** | | [optional] +**Value** | Pointer to **string** | | [optional] + +## Methods + +### NewDtoCreateLabelRequest + +`func NewDtoCreateLabelRequest() *DtoCreateLabelRequest` + +NewDtoCreateLabelRequest instantiates a new DtoCreateLabelRequest object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewDtoCreateLabelRequestWithDefaults + +`func NewDtoCreateLabelRequestWithDefaults() *DtoCreateLabelRequest` + +NewDtoCreateLabelRequestWithDefaults instantiates a new DtoCreateLabelRequest object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetKey + +`func (o *DtoCreateLabelRequest) GetKey() string` + +GetKey returns the Key field if non-nil, zero value otherwise. + +### GetKeyOk + +`func (o *DtoCreateLabelRequest) GetKeyOk() (*string, bool)` + +GetKeyOk returns a tuple with the Key field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetKey + +`func (o *DtoCreateLabelRequest) SetKey(v string)` + +SetKey sets Key field to given value. + +### HasKey + +`func (o *DtoCreateLabelRequest) HasKey() bool` + +HasKey returns a boolean if a field has been set. + +### GetValue + +`func (o *DtoCreateLabelRequest) GetValue() string` + +GetValue returns the Value field if non-nil, zero value otherwise. + +### GetValueOk + +`func (o *DtoCreateLabelRequest) GetValueOk() (*string, bool)` + +GetValueOk returns a tuple with the Value field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetValue + +`func (o *DtoCreateLabelRequest) SetValue(v string)` + +SetValue sets Value field to given value. + +### HasValue + +`func (o *DtoCreateLabelRequest) HasValue() bool` + +HasValue returns a boolean if a field has been set. + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/go/docs/DtoLabelResponse.md b/sdk/go/docs/DtoLabelResponse.md new file mode 100644 index 0000000..83a2b9a --- /dev/null +++ b/sdk/go/docs/DtoLabelResponse.md @@ -0,0 +1,108 @@ +# DtoLabelResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Id** | Pointer to **string** | | [optional] +**Key** | Pointer to **string** | | [optional] +**Value** | Pointer to **string** | | [optional] + +## Methods + +### NewDtoLabelResponse + +`func NewDtoLabelResponse() *DtoLabelResponse` + +NewDtoLabelResponse instantiates a new DtoLabelResponse object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewDtoLabelResponseWithDefaults + +`func NewDtoLabelResponseWithDefaults() *DtoLabelResponse` + +NewDtoLabelResponseWithDefaults instantiates a new DtoLabelResponse object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetId + +`func (o *DtoLabelResponse) GetId() string` + +GetId returns the Id field if non-nil, zero value otherwise. + +### GetIdOk + +`func (o *DtoLabelResponse) GetIdOk() (*string, bool)` + +GetIdOk returns a tuple with the Id field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetId + +`func (o *DtoLabelResponse) SetId(v string)` + +SetId sets Id field to given value. + +### HasId + +`func (o *DtoLabelResponse) HasId() bool` + +HasId returns a boolean if a field has been set. + +### GetKey + +`func (o *DtoLabelResponse) GetKey() string` + +GetKey returns the Key field if non-nil, zero value otherwise. + +### GetKeyOk + +`func (o *DtoLabelResponse) GetKeyOk() (*string, bool)` + +GetKeyOk returns a tuple with the Key field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetKey + +`func (o *DtoLabelResponse) SetKey(v string)` + +SetKey sets Key field to given value. + +### HasKey + +`func (o *DtoLabelResponse) HasKey() bool` + +HasKey returns a boolean if a field has been set. + +### GetValue + +`func (o *DtoLabelResponse) GetValue() string` + +GetValue returns the Value field if non-nil, zero value otherwise. + +### GetValueOk + +`func (o *DtoLabelResponse) GetValueOk() (*string, bool)` + +GetValueOk returns a tuple with the Value field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetValue + +`func (o *DtoLabelResponse) SetValue(v string)` + +SetValue sets Value field to given value. + +### HasValue + +`func (o *DtoLabelResponse) HasValue() bool` + +HasValue returns a boolean if a field has been set. + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/go/docs/DtoUpdateLabelRequest.md b/sdk/go/docs/DtoUpdateLabelRequest.md new file mode 100644 index 0000000..a32d3c7 --- /dev/null +++ b/sdk/go/docs/DtoUpdateLabelRequest.md @@ -0,0 +1,82 @@ +# DtoUpdateLabelRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Key** | Pointer to **string** | | [optional] +**Value** | Pointer to **string** | | [optional] + +## Methods + +### NewDtoUpdateLabelRequest + +`func NewDtoUpdateLabelRequest() *DtoUpdateLabelRequest` + +NewDtoUpdateLabelRequest instantiates a new DtoUpdateLabelRequest object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewDtoUpdateLabelRequestWithDefaults + +`func NewDtoUpdateLabelRequestWithDefaults() *DtoUpdateLabelRequest` + +NewDtoUpdateLabelRequestWithDefaults instantiates a new DtoUpdateLabelRequest object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetKey + +`func (o *DtoUpdateLabelRequest) GetKey() string` + +GetKey returns the Key field if non-nil, zero value otherwise. + +### GetKeyOk + +`func (o *DtoUpdateLabelRequest) GetKeyOk() (*string, bool)` + +GetKeyOk returns a tuple with the Key field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetKey + +`func (o *DtoUpdateLabelRequest) SetKey(v string)` + +SetKey sets Key field to given value. + +### HasKey + +`func (o *DtoUpdateLabelRequest) HasKey() bool` + +HasKey returns a boolean if a field has been set. + +### GetValue + +`func (o *DtoUpdateLabelRequest) GetValue() string` + +GetValue returns the Value field if non-nil, zero value otherwise. + +### GetValueOk + +`func (o *DtoUpdateLabelRequest) GetValueOk() (*string, bool)` + +GetValueOk returns a tuple with the Value field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetValue + +`func (o *DtoUpdateLabelRequest) SetValue(v string)` + +SetValue sets Value field to given value. + +### HasValue + +`func (o *DtoUpdateLabelRequest) HasValue() bool` + +HasValue returns a boolean if a field has been set. + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdk/go/docs/LabelsAPI.md b/sdk/go/docs/LabelsAPI.md new file mode 100644 index 0000000..bbd5121 --- /dev/null +++ b/sdk/go/docs/LabelsAPI.md @@ -0,0 +1,371 @@ +# \LabelsAPI + +All URIs are relative to *http://localhost:8080/api/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**CreateLabel**](LabelsAPI.md#CreateLabel) | **Post** /labels | Create label (org scoped) +[**DeleteLabel**](LabelsAPI.md#DeleteLabel) | **Delete** /labels/{id} | Delete label (org scoped) +[**GetLabel**](LabelsAPI.md#GetLabel) | **Get** /labels/{id} | Get label by ID (org scoped) +[**ListLabels**](LabelsAPI.md#ListLabels) | **Get** /labels | List node labels (org scoped) +[**UpdateLabel**](LabelsAPI.md#UpdateLabel) | **Patch** /labels/{id} | Update label (org scoped) + + + +## CreateLabel + +> DtoLabelResponse CreateLabel(ctx).Body(body).XOrgID(xOrgID).Execute() + +Create label (org scoped) + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "github.com/glueops/autoglue-sdk" +) + +func main() { + body := *openapiclient.NewDtoCreateLabelRequest() // DtoCreateLabelRequest | Label payload + xOrgID := "xOrgID_example" // string | Organization UUID (optional) + + configuration := openapiclient.NewConfiguration() + apiClient := openapiclient.NewAPIClient(configuration) + resp, r, err := apiClient.LabelsAPI.CreateLabel(context.Background()).Body(body).XOrgID(xOrgID).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `LabelsAPI.CreateLabel``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `CreateLabel`: DtoLabelResponse + fmt.Fprintf(os.Stdout, "Response from `LabelsAPI.CreateLabel`: %v\n", resp) +} +``` + +### Path Parameters + + + +### Other Parameters + +Other parameters are passed through a pointer to a apiCreateLabelRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **body** | [**DtoCreateLabelRequest**](DtoCreateLabelRequest.md) | Label payload | + **xOrgID** | **string** | Organization UUID | + +### Return type + +[**DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + +## DeleteLabel + +> string DeleteLabel(ctx, id).XOrgID(xOrgID).Execute() + +Delete label (org scoped) + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "github.com/glueops/autoglue-sdk" +) + +func main() { + id := "id_example" // string | Label ID (UUID) + xOrgID := "xOrgID_example" // string | Organization UUID (optional) + + configuration := openapiclient.NewConfiguration() + apiClient := openapiclient.NewAPIClient(configuration) + resp, r, err := apiClient.LabelsAPI.DeleteLabel(context.Background(), id).XOrgID(xOrgID).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `LabelsAPI.DeleteLabel``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `DeleteLabel`: string + fmt.Fprintf(os.Stdout, "Response from `LabelsAPI.DeleteLabel`: %v\n", resp) +} +``` + +### Path Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. +**id** | **string** | Label ID (UUID) | + +### Other Parameters + +Other parameters are passed through a pointer to a apiDeleteLabelRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + **xOrgID** | **string** | Organization UUID | + +### Return type + +**string** + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + +## GetLabel + +> DtoLabelResponse GetLabel(ctx, id).XOrgID(xOrgID).Execute() + +Get label by ID (org scoped) + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "github.com/glueops/autoglue-sdk" +) + +func main() { + id := "id_example" // string | Label ID (UUID) + xOrgID := "xOrgID_example" // string | Organization UUID (optional) + + configuration := openapiclient.NewConfiguration() + apiClient := openapiclient.NewAPIClient(configuration) + resp, r, err := apiClient.LabelsAPI.GetLabel(context.Background(), id).XOrgID(xOrgID).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `LabelsAPI.GetLabel``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `GetLabel`: DtoLabelResponse + fmt.Fprintf(os.Stdout, "Response from `LabelsAPI.GetLabel`: %v\n", resp) +} +``` + +### Path Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. +**id** | **string** | Label ID (UUID) | + +### Other Parameters + +Other parameters are passed through a pointer to a apiGetLabelRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + **xOrgID** | **string** | Organization UUID | + +### Return type + +[**DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + +## ListLabels + +> []DtoLabelResponse ListLabels(ctx).XOrgID(xOrgID).Key(key).Value(value).Q(q).Execute() + +List node labels (org scoped) + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "github.com/glueops/autoglue-sdk" +) + +func main() { + xOrgID := "xOrgID_example" // string | Organization UUID (optional) + key := "key_example" // string | Exact key (optional) + value := "value_example" // string | Exact value (optional) + q := "q_example" // string | Key contains (case-insensitive) (optional) + + configuration := openapiclient.NewConfiguration() + apiClient := openapiclient.NewAPIClient(configuration) + resp, r, err := apiClient.LabelsAPI.ListLabels(context.Background()).XOrgID(xOrgID).Key(key).Value(value).Q(q).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `LabelsAPI.ListLabels``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `ListLabels`: []DtoLabelResponse + fmt.Fprintf(os.Stdout, "Response from `LabelsAPI.ListLabels`: %v\n", resp) +} +``` + +### Path Parameters + + + +### Other Parameters + +Other parameters are passed through a pointer to a apiListLabelsRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **xOrgID** | **string** | Organization UUID | + **key** | **string** | Exact key | + **value** | **string** | Exact value | + **q** | **string** | Key contains (case-insensitive) | + +### Return type + +[**[]DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + +## UpdateLabel + +> DtoLabelResponse UpdateLabel(ctx, id).Body(body).XOrgID(xOrgID).Execute() + +Update label (org scoped) + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "github.com/glueops/autoglue-sdk" +) + +func main() { + id := "id_example" // string | Label ID (UUID) + body := *openapiclient.NewDtoUpdateLabelRequest() // DtoUpdateLabelRequest | Fields to update + xOrgID := "xOrgID_example" // string | Organization UUID (optional) + + configuration := openapiclient.NewConfiguration() + apiClient := openapiclient.NewAPIClient(configuration) + resp, r, err := apiClient.LabelsAPI.UpdateLabel(context.Background(), id).Body(body).XOrgID(xOrgID).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `LabelsAPI.UpdateLabel``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `UpdateLabel`: DtoLabelResponse + fmt.Fprintf(os.Stdout, "Response from `LabelsAPI.UpdateLabel`: %v\n", resp) +} +``` + +### Path Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. +**id** | **string** | Label ID (UUID) | + +### Other Parameters + +Other parameters are passed through a pointer to a apiUpdateLabelRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + **body** | [**DtoUpdateLabelRequest**](DtoUpdateLabelRequest.md) | Fields to update | + **xOrgID** | **string** | Organization UUID | + +### Return type + +[**DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + diff --git a/sdk/go/model_dto_create_label_request.go b/sdk/go/model_dto_create_label_request.go new file mode 100644 index 0000000..4ce183d --- /dev/null +++ b/sdk/go/model_dto_create_label_request.go @@ -0,0 +1,160 @@ +/* +AutoGlue API + +API for managing K3s clusters across cloud providers + +API version: 1.0 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package autoglue + +import ( + "encoding/json" +) + +// checks if the DtoCreateLabelRequest type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &DtoCreateLabelRequest{} + +// DtoCreateLabelRequest struct for DtoCreateLabelRequest +type DtoCreateLabelRequest struct { + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` +} + +// NewDtoCreateLabelRequest instantiates a new DtoCreateLabelRequest object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewDtoCreateLabelRequest() *DtoCreateLabelRequest { + this := DtoCreateLabelRequest{} + return &this +} + +// NewDtoCreateLabelRequestWithDefaults instantiates a new DtoCreateLabelRequest object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewDtoCreateLabelRequestWithDefaults() *DtoCreateLabelRequest { + this := DtoCreateLabelRequest{} + return &this +} + +// GetKey returns the Key field value if set, zero value otherwise. +func (o *DtoCreateLabelRequest) GetKey() string { + if o == nil || IsNil(o.Key) { + var ret string + return ret + } + return *o.Key +} + +// GetKeyOk returns a tuple with the Key field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoCreateLabelRequest) GetKeyOk() (*string, bool) { + if o == nil || IsNil(o.Key) { + return nil, false + } + return o.Key, true +} + +// HasKey returns a boolean if a field has been set. +func (o *DtoCreateLabelRequest) HasKey() bool { + if o != nil && !IsNil(o.Key) { + return true + } + + return false +} + +// SetKey gets a reference to the given string and assigns it to the Key field. +func (o *DtoCreateLabelRequest) SetKey(v string) { + o.Key = &v +} + +// GetValue returns the Value field value if set, zero value otherwise. +func (o *DtoCreateLabelRequest) GetValue() string { + if o == nil || IsNil(o.Value) { + var ret string + return ret + } + return *o.Value +} + +// GetValueOk returns a tuple with the Value field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoCreateLabelRequest) GetValueOk() (*string, bool) { + if o == nil || IsNil(o.Value) { + return nil, false + } + return o.Value, true +} + +// HasValue returns a boolean if a field has been set. +func (o *DtoCreateLabelRequest) HasValue() bool { + if o != nil && !IsNil(o.Value) { + return true + } + + return false +} + +// SetValue gets a reference to the given string and assigns it to the Value field. +func (o *DtoCreateLabelRequest) SetValue(v string) { + o.Value = &v +} + +func (o DtoCreateLabelRequest) MarshalJSON() ([]byte, error) { + toSerialize, err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o DtoCreateLabelRequest) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Key) { + toSerialize["key"] = o.Key + } + if !IsNil(o.Value) { + toSerialize["value"] = o.Value + } + return toSerialize, nil +} + +type NullableDtoCreateLabelRequest struct { + value *DtoCreateLabelRequest + isSet bool +} + +func (v NullableDtoCreateLabelRequest) Get() *DtoCreateLabelRequest { + return v.value +} + +func (v *NullableDtoCreateLabelRequest) Set(val *DtoCreateLabelRequest) { + v.value = val + v.isSet = true +} + +func (v NullableDtoCreateLabelRequest) IsSet() bool { + return v.isSet +} + +func (v *NullableDtoCreateLabelRequest) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableDtoCreateLabelRequest(val *DtoCreateLabelRequest) *NullableDtoCreateLabelRequest { + return &NullableDtoCreateLabelRequest{value: val, isSet: true} +} + +func (v NullableDtoCreateLabelRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableDtoCreateLabelRequest) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/sdk/go/model_dto_label_response.go b/sdk/go/model_dto_label_response.go new file mode 100644 index 0000000..24dc623 --- /dev/null +++ b/sdk/go/model_dto_label_response.go @@ -0,0 +1,196 @@ +/* +AutoGlue API + +API for managing K3s clusters across cloud providers + +API version: 1.0 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package autoglue + +import ( + "encoding/json" +) + +// checks if the DtoLabelResponse type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &DtoLabelResponse{} + +// DtoLabelResponse struct for DtoLabelResponse +type DtoLabelResponse struct { + Id *string `json:"id,omitempty"` + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` +} + +// NewDtoLabelResponse instantiates a new DtoLabelResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewDtoLabelResponse() *DtoLabelResponse { + this := DtoLabelResponse{} + return &this +} + +// NewDtoLabelResponseWithDefaults instantiates a new DtoLabelResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewDtoLabelResponseWithDefaults() *DtoLabelResponse { + this := DtoLabelResponse{} + return &this +} + +// GetId returns the Id field value if set, zero value otherwise. +func (o *DtoLabelResponse) GetId() string { + if o == nil || IsNil(o.Id) { + var ret string + return ret + } + return *o.Id +} + +// GetIdOk returns a tuple with the Id field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoLabelResponse) GetIdOk() (*string, bool) { + if o == nil || IsNil(o.Id) { + return nil, false + } + return o.Id, true +} + +// HasId returns a boolean if a field has been set. +func (o *DtoLabelResponse) HasId() bool { + if o != nil && !IsNil(o.Id) { + return true + } + + return false +} + +// SetId gets a reference to the given string and assigns it to the Id field. +func (o *DtoLabelResponse) SetId(v string) { + o.Id = &v +} + +// GetKey returns the Key field value if set, zero value otherwise. +func (o *DtoLabelResponse) GetKey() string { + if o == nil || IsNil(o.Key) { + var ret string + return ret + } + return *o.Key +} + +// GetKeyOk returns a tuple with the Key field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoLabelResponse) GetKeyOk() (*string, bool) { + if o == nil || IsNil(o.Key) { + return nil, false + } + return o.Key, true +} + +// HasKey returns a boolean if a field has been set. +func (o *DtoLabelResponse) HasKey() bool { + if o != nil && !IsNil(o.Key) { + return true + } + + return false +} + +// SetKey gets a reference to the given string and assigns it to the Key field. +func (o *DtoLabelResponse) SetKey(v string) { + o.Key = &v +} + +// GetValue returns the Value field value if set, zero value otherwise. +func (o *DtoLabelResponse) GetValue() string { + if o == nil || IsNil(o.Value) { + var ret string + return ret + } + return *o.Value +} + +// GetValueOk returns a tuple with the Value field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoLabelResponse) GetValueOk() (*string, bool) { + if o == nil || IsNil(o.Value) { + return nil, false + } + return o.Value, true +} + +// HasValue returns a boolean if a field has been set. +func (o *DtoLabelResponse) HasValue() bool { + if o != nil && !IsNil(o.Value) { + return true + } + + return false +} + +// SetValue gets a reference to the given string and assigns it to the Value field. +func (o *DtoLabelResponse) SetValue(v string) { + o.Value = &v +} + +func (o DtoLabelResponse) MarshalJSON() ([]byte, error) { + toSerialize, err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o DtoLabelResponse) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Id) { + toSerialize["id"] = o.Id + } + if !IsNil(o.Key) { + toSerialize["key"] = o.Key + } + if !IsNil(o.Value) { + toSerialize["value"] = o.Value + } + return toSerialize, nil +} + +type NullableDtoLabelResponse struct { + value *DtoLabelResponse + isSet bool +} + +func (v NullableDtoLabelResponse) Get() *DtoLabelResponse { + return v.value +} + +func (v *NullableDtoLabelResponse) Set(val *DtoLabelResponse) { + v.value = val + v.isSet = true +} + +func (v NullableDtoLabelResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableDtoLabelResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableDtoLabelResponse(val *DtoLabelResponse) *NullableDtoLabelResponse { + return &NullableDtoLabelResponse{value: val, isSet: true} +} + +func (v NullableDtoLabelResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableDtoLabelResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/sdk/go/model_dto_update_label_request.go b/sdk/go/model_dto_update_label_request.go new file mode 100644 index 0000000..f3106de --- /dev/null +++ b/sdk/go/model_dto_update_label_request.go @@ -0,0 +1,160 @@ +/* +AutoGlue API + +API for managing K3s clusters across cloud providers + +API version: 1.0 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package autoglue + +import ( + "encoding/json" +) + +// checks if the DtoUpdateLabelRequest type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &DtoUpdateLabelRequest{} + +// DtoUpdateLabelRequest struct for DtoUpdateLabelRequest +type DtoUpdateLabelRequest struct { + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` +} + +// NewDtoUpdateLabelRequest instantiates a new DtoUpdateLabelRequest object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewDtoUpdateLabelRequest() *DtoUpdateLabelRequest { + this := DtoUpdateLabelRequest{} + return &this +} + +// NewDtoUpdateLabelRequestWithDefaults instantiates a new DtoUpdateLabelRequest object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewDtoUpdateLabelRequestWithDefaults() *DtoUpdateLabelRequest { + this := DtoUpdateLabelRequest{} + return &this +} + +// GetKey returns the Key field value if set, zero value otherwise. +func (o *DtoUpdateLabelRequest) GetKey() string { + if o == nil || IsNil(o.Key) { + var ret string + return ret + } + return *o.Key +} + +// GetKeyOk returns a tuple with the Key field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoUpdateLabelRequest) GetKeyOk() (*string, bool) { + if o == nil || IsNil(o.Key) { + return nil, false + } + return o.Key, true +} + +// HasKey returns a boolean if a field has been set. +func (o *DtoUpdateLabelRequest) HasKey() bool { + if o != nil && !IsNil(o.Key) { + return true + } + + return false +} + +// SetKey gets a reference to the given string and assigns it to the Key field. +func (o *DtoUpdateLabelRequest) SetKey(v string) { + o.Key = &v +} + +// GetValue returns the Value field value if set, zero value otherwise. +func (o *DtoUpdateLabelRequest) GetValue() string { + if o == nil || IsNil(o.Value) { + var ret string + return ret + } + return *o.Value +} + +// GetValueOk returns a tuple with the Value field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DtoUpdateLabelRequest) GetValueOk() (*string, bool) { + if o == nil || IsNil(o.Value) { + return nil, false + } + return o.Value, true +} + +// HasValue returns a boolean if a field has been set. +func (o *DtoUpdateLabelRequest) HasValue() bool { + if o != nil && !IsNil(o.Value) { + return true + } + + return false +} + +// SetValue gets a reference to the given string and assigns it to the Value field. +func (o *DtoUpdateLabelRequest) SetValue(v string) { + o.Value = &v +} + +func (o DtoUpdateLabelRequest) MarshalJSON() ([]byte, error) { + toSerialize, err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o DtoUpdateLabelRequest) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Key) { + toSerialize["key"] = o.Key + } + if !IsNil(o.Value) { + toSerialize["value"] = o.Value + } + return toSerialize, nil +} + +type NullableDtoUpdateLabelRequest struct { + value *DtoUpdateLabelRequest + isSet bool +} + +func (v NullableDtoUpdateLabelRequest) Get() *DtoUpdateLabelRequest { + return v.value +} + +func (v *NullableDtoUpdateLabelRequest) Set(val *DtoUpdateLabelRequest) { + v.value = val + v.isSet = true +} + +func (v NullableDtoUpdateLabelRequest) IsSet() bool { + return v.isSet +} + +func (v *NullableDtoUpdateLabelRequest) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableDtoUpdateLabelRequest(val *DtoUpdateLabelRequest) *NullableDtoUpdateLabelRequest { + return &NullableDtoUpdateLabelRequest{value: val, isSet: true} +} + +func (v NullableDtoUpdateLabelRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableDtoUpdateLabelRequest) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/sdk/go/test/api_labels_test.go b/sdk/go/test/api_labels_test.go new file mode 100644 index 0000000..e756e38 --- /dev/null +++ b/sdk/go/test/api_labels_test.go @@ -0,0 +1,91 @@ +/* +AutoGlue API + +Testing LabelsAPIService + +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); + +package autoglue + +import ( + "context" + openapiclient "github.com/glueops/autoglue-sdk" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func Test_autoglue_LabelsAPIService(t *testing.T) { + + configuration := openapiclient.NewConfiguration() + apiClient := openapiclient.NewAPIClient(configuration) + + t.Run("Test LabelsAPIService CreateLabel", func(t *testing.T) { + + t.Skip("skip test") // remove to run test + + resp, httpRes, err := apiClient.LabelsAPI.CreateLabel(context.Background()).Execute() + + require.Nil(t, err) + require.NotNil(t, resp) + assert.Equal(t, 200, httpRes.StatusCode) + + }) + + t.Run("Test LabelsAPIService DeleteLabel", func(t *testing.T) { + + t.Skip("skip test") // remove to run test + + var id string + + resp, httpRes, err := apiClient.LabelsAPI.DeleteLabel(context.Background(), id).Execute() + + require.Nil(t, err) + require.NotNil(t, resp) + assert.Equal(t, 200, httpRes.StatusCode) + + }) + + t.Run("Test LabelsAPIService GetLabel", func(t *testing.T) { + + t.Skip("skip test") // remove to run test + + var id string + + resp, httpRes, err := apiClient.LabelsAPI.GetLabel(context.Background(), id).Execute() + + require.Nil(t, err) + require.NotNil(t, resp) + assert.Equal(t, 200, httpRes.StatusCode) + + }) + + t.Run("Test LabelsAPIService ListLabels", func(t *testing.T) { + + t.Skip("skip test") // remove to run test + + resp, httpRes, err := apiClient.LabelsAPI.ListLabels(context.Background()).Execute() + + require.Nil(t, err) + require.NotNil(t, resp) + assert.Equal(t, 200, httpRes.StatusCode) + + }) + + t.Run("Test LabelsAPIService UpdateLabel", func(t *testing.T) { + + t.Skip("skip test") // remove to run test + + var id string + + resp, httpRes, err := apiClient.LabelsAPI.UpdateLabel(context.Background(), id).Execute() + + require.Nil(t, err) + require.NotNil(t, resp) + assert.Equal(t, 200, httpRes.StatusCode) + + }) + +} diff --git a/sdk/ts/.openapi-generator/FILES b/sdk/ts/.openapi-generator/FILES index 743d966..476ba7d 100644 --- a/sdk/ts/.openapi-generator/FILES +++ b/sdk/ts/.openapi-generator/FILES @@ -4,11 +4,13 @@ README.md docs/AuthApi.md docs/DtoAuthStartResponse.md +docs/DtoCreateLabelRequest.md docs/DtoCreateSSHRequest.md docs/DtoCreateServerRequest.md docs/DtoCreateTaintRequest.md docs/DtoJWK.md docs/DtoJWKS.md +docs/DtoLabelResponse.md docs/DtoLogoutRequest.md docs/DtoRefreshRequest.md docs/DtoServerResponse.md @@ -16,6 +18,7 @@ docs/DtoSshResponse.md docs/DtoSshRevealResponse.md docs/DtoTaintResponse.md docs/DtoTokenPair.md +docs/DtoUpdateLabelRequest.md docs/DtoUpdateServerRequest.md docs/DtoUpdateTaintRequest.md docs/HandlersCreateUserKeyRequest.md @@ -28,6 +31,7 @@ docs/HandlersOrgKeyCreateResp.md docs/HandlersOrgUpdateReq.md docs/HandlersUpdateMeRequest.md docs/HandlersUserAPIKeyOut.md +docs/LabelsApi.md docs/MeAPIKeysApi.md docs/MeApi.md docs/ModelsAPIKey.md @@ -41,6 +45,7 @@ docs/TaintsApi.md docs/UtilsErrorResponse.md package.json src/apis/AuthApi.ts +src/apis/LabelsApi.ts src/apis/MeAPIKeysApi.ts src/apis/MeApi.ts src/apis/OrgsApi.ts @@ -50,11 +55,13 @@ src/apis/TaintsApi.ts src/apis/index.ts src/index.ts src/models/DtoAuthStartResponse.ts +src/models/DtoCreateLabelRequest.ts src/models/DtoCreateSSHRequest.ts src/models/DtoCreateServerRequest.ts src/models/DtoCreateTaintRequest.ts src/models/DtoJWK.ts src/models/DtoJWKS.ts +src/models/DtoLabelResponse.ts src/models/DtoLogoutRequest.ts src/models/DtoRefreshRequest.ts src/models/DtoServerResponse.ts @@ -62,6 +69,7 @@ src/models/DtoSshResponse.ts src/models/DtoSshRevealResponse.ts src/models/DtoTaintResponse.ts src/models/DtoTokenPair.ts +src/models/DtoUpdateLabelRequest.ts src/models/DtoUpdateServerRequest.ts src/models/DtoUpdateTaintRequest.ts src/models/HandlersCreateUserKeyRequest.ts diff --git a/sdk/ts/README.md b/sdk/ts/README.md index 188d4ff..a52decc 100644 --- a/sdk/ts/README.md +++ b/sdk/ts/README.md @@ -50,6 +50,11 @@ All URIs are relative to _http://localhost:8080/api/v1_ | _AuthApi_ | [**getJWKS**](docs/AuthApi.md#getjwks) | **GET** /.well-known/jwks.json | Get JWKS | | _AuthApi_ | [**logout**](docs/AuthApi.md#logout) | **POST** /auth/logout | Revoke refresh token family (logout everywhere) | | _AuthApi_ | [**refresh**](docs/AuthApi.md#refresh) | **POST** /auth/refresh | Rotate refresh token | +| _LabelsApi_ | [**createLabel**](docs/LabelsApi.md#createlabel) | **POST** /labels | Create label (org scoped) | +| _LabelsApi_ | [**deleteLabel**](docs/LabelsApi.md#deletelabel) | **DELETE** /labels/{id} | Delete label (org scoped) | +| _LabelsApi_ | [**getLabel**](docs/LabelsApi.md#getlabel) | **GET** /labels/{id} | Get label by ID (org scoped) | +| _LabelsApi_ | [**listLabels**](docs/LabelsApi.md#listlabels) | **GET** /labels | List node labels (org scoped) | +| _LabelsApi_ | [**updateLabel**](docs/LabelsApi.md#updatelabel) | **PATCH** /labels/{id} | Update label (org scoped) | | _MeApi_ | [**getMe**](docs/MeApi.md#getme) | **GET** /me | Get current user profile | | _MeApi_ | [**updateMe**](docs/MeApi.md#updateme) | **PATCH** /me | Update current user profile | | _MeAPIKeysApi_ | [**createUserAPIKey**](docs/MeAPIKeysApi.md#createuserapikey) | **POST** /me/api-keys | Create a new user API key | @@ -85,11 +90,13 @@ All URIs are relative to _http://localhost:8080/api/v1_ ### Models - [DtoAuthStartResponse](docs/DtoAuthStartResponse.md) +- [DtoCreateLabelRequest](docs/DtoCreateLabelRequest.md) - [DtoCreateSSHRequest](docs/DtoCreateSSHRequest.md) - [DtoCreateServerRequest](docs/DtoCreateServerRequest.md) - [DtoCreateTaintRequest](docs/DtoCreateTaintRequest.md) - [DtoJWK](docs/DtoJWK.md) - [DtoJWKS](docs/DtoJWKS.md) +- [DtoLabelResponse](docs/DtoLabelResponse.md) - [DtoLogoutRequest](docs/DtoLogoutRequest.md) - [DtoRefreshRequest](docs/DtoRefreshRequest.md) - [DtoServerResponse](docs/DtoServerResponse.md) @@ -97,6 +104,7 @@ All URIs are relative to _http://localhost:8080/api/v1_ - [DtoSshRevealResponse](docs/DtoSshRevealResponse.md) - [DtoTaintResponse](docs/DtoTaintResponse.md) - [DtoTokenPair](docs/DtoTokenPair.md) +- [DtoUpdateLabelRequest](docs/DtoUpdateLabelRequest.md) - [DtoUpdateServerRequest](docs/DtoUpdateServerRequest.md) - [DtoUpdateTaintRequest](docs/DtoUpdateTaintRequest.md) - [HandlersCreateUserKeyRequest](docs/HandlersCreateUserKeyRequest.md) diff --git a/sdk/ts/docs/DtoCreateLabelRequest.md b/sdk/ts/docs/DtoCreateLabelRequest.md new file mode 100644 index 0000000..2dcc46a --- /dev/null +++ b/sdk/ts/docs/DtoCreateLabelRequest.md @@ -0,0 +1,32 @@ +# DtoCreateLabelRequest + +## Properties + +| Name | Type | +| ------- | ------ | +| `key` | string | +| `value` | string | + +## Example + +```typescript +import type { DtoCreateLabelRequest } from "@glueops/autoglue-sdk"; + +// TODO: Update the object below with actual values +const example = { + key: null, + value: null, +} satisfies DtoCreateLabelRequest; + +console.log(example); + +// Convert the instance to a JSON string +const exampleJSON: string = JSON.stringify(example); +console.log(exampleJSON); + +// Parse the JSON string back to an object +const exampleParsed = JSON.parse(exampleJSON) as DtoCreateLabelRequest; +console.log(exampleParsed); +``` + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) diff --git a/sdk/ts/docs/DtoLabelResponse.md b/sdk/ts/docs/DtoLabelResponse.md new file mode 100644 index 0000000..f790095 --- /dev/null +++ b/sdk/ts/docs/DtoLabelResponse.md @@ -0,0 +1,34 @@ +# DtoLabelResponse + +## Properties + +| Name | Type | +| ------- | ------ | +| `id` | string | +| `key` | string | +| `value` | string | + +## Example + +```typescript +import type { DtoLabelResponse } from "@glueops/autoglue-sdk"; + +// TODO: Update the object below with actual values +const example = { + id: null, + key: null, + value: null, +} satisfies DtoLabelResponse; + +console.log(example); + +// Convert the instance to a JSON string +const exampleJSON: string = JSON.stringify(example); +console.log(exampleJSON); + +// Parse the JSON string back to an object +const exampleParsed = JSON.parse(exampleJSON) as DtoLabelResponse; +console.log(exampleParsed); +``` + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) diff --git a/sdk/ts/docs/DtoUpdateLabelRequest.md b/sdk/ts/docs/DtoUpdateLabelRequest.md new file mode 100644 index 0000000..f2e96ea --- /dev/null +++ b/sdk/ts/docs/DtoUpdateLabelRequest.md @@ -0,0 +1,32 @@ +# DtoUpdateLabelRequest + +## Properties + +| Name | Type | +| ------- | ------ | +| `key` | string | +| `value` | string | + +## Example + +```typescript +import type { DtoUpdateLabelRequest } from "@glueops/autoglue-sdk"; + +// TODO: Update the object below with actual values +const example = { + key: null, + value: null, +} satisfies DtoUpdateLabelRequest; + +console.log(example); + +// Convert the instance to a JSON string +const exampleJSON: string = JSON.stringify(example); +console.log(exampleJSON); + +// Parse the JSON string back to an object +const exampleParsed = JSON.parse(exampleJSON) as DtoUpdateLabelRequest; +console.log(exampleParsed); +``` + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) diff --git a/sdk/ts/docs/LabelsApi.md b/sdk/ts/docs/LabelsApi.md new file mode 100644 index 0000000..0f24121 --- /dev/null +++ b/sdk/ts/docs/LabelsApi.md @@ -0,0 +1,412 @@ +# LabelsApi + +All URIs are relative to _http://localhost:8080/api/v1_ + +| Method | HTTP request | Description | +| ------------------------------------------- | ----------------------- | ----------------------------- | +| [**createLabel**](LabelsApi.md#createlabel) | **POST** /labels | Create label (org scoped) | +| [**deleteLabel**](LabelsApi.md#deletelabel) | **DELETE** /labels/{id} | Delete label (org scoped) | +| [**getLabel**](LabelsApi.md#getlabel) | **GET** /labels/{id} | Get label by ID (org scoped) | +| [**listLabels**](LabelsApi.md#listlabels) | **GET** /labels | List node labels (org scoped) | +| [**updateLabel**](LabelsApi.md#updatelabel) | **PATCH** /labels/{id} | Update label (org scoped) | + +## createLabel + +> DtoLabelResponse createLabel(body, xOrgID) + +Create label (org scoped) + +Creates a label. + +### Example + +```ts +import { + Configuration, + LabelsApi, +} from '@glueops/autoglue-sdk'; +import type { CreateLabelRequest } from '@glueops/autoglue-sdk'; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // DtoCreateLabelRequest | Label payload + body: ..., + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + } satisfies CreateLabelRequest; + + try { + const data = await api.createLabel(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + +| Name | Type | Description | Notes | +| ---------- | ------------------------------------------------- | ----------------- | ------------------------------------ | +| **body** | [DtoCreateLabelRequest](DtoCreateLabelRequest.md) | Label payload | | +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | + +### Return type + +[**DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: `application/json` +- **Accept**: `application/json` + +### HTTP response details + +| Status code | Description | Response headers | +| ----------- | ----------------------------------------------------- | ---------------- | +| **201** | Created | - | +| **400** | invalid json / missing fields / invalid node_pool_ids | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **500** | create failed | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + +## deleteLabel + +> string deleteLabel(id, xOrgID) + +Delete label (org scoped) + +Permanently deletes the label. + +### Example + +```ts +import { Configuration, LabelsApi } from "@glueops/autoglue-sdk"; +import type { DeleteLabelRequest } from "@glueops/autoglue-sdk"; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // string | Label ID (UUID) + id: id_example, + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + } satisfies DeleteLabelRequest; + + try { + const data = await api.deleteLabel(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + +| Name | Type | Description | Notes | +| ---------- | -------- | ----------------- | ------------------------------------ | +| **id** | `string` | Label ID (UUID) | [Defaults to `undefined`] | +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | + +### Return type + +**string** + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `application/json` + +### HTTP response details + +| Status code | Description | Response headers | +| ----------- | --------------------- | ---------------- | +| **204** | No Content | - | +| **400** | invalid id | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **500** | delete failed | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + +## getLabel + +> DtoLabelResponse getLabel(id, xOrgID) + +Get label by ID (org scoped) + +Returns one label. + +### Example + +```ts +import { Configuration, LabelsApi } from "@glueops/autoglue-sdk"; +import type { GetLabelRequest } from "@glueops/autoglue-sdk"; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // string | Label ID (UUID) + id: id_example, + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + } satisfies GetLabelRequest; + + try { + const data = await api.getLabel(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + +| Name | Type | Description | Notes | +| ---------- | -------- | ----------------- | ------------------------------------ | +| **id** | `string` | Label ID (UUID) | [Defaults to `undefined`] | +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | + +### Return type + +[**DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `application/json` + +### HTTP response details + +| Status code | Description | Response headers | +| ----------- | --------------------- | ---------------- | +| **200** | OK | - | +| **400** | invalid id | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **404** | not found | - | +| **500** | fetch failed | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + +## listLabels + +> Array<DtoLabelResponse> listLabels(xOrgID, key, value, q) + +List node labels (org scoped) + +Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups. + +### Example + +```ts +import { Configuration, LabelsApi } from "@glueops/autoglue-sdk"; +import type { ListLabelsRequest } from "@glueops/autoglue-sdk"; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + // string | Exact key (optional) + key: key_example, + // string | Exact value (optional) + value: value_example, + // string | Key contains (case-insensitive) (optional) + q: q_example, + } satisfies ListLabelsRequest; + + try { + const data = await api.listLabels(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + +| Name | Type | Description | Notes | +| ---------- | -------- | ------------------------------- | ------------------------------------ | +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | +| **key** | `string` | Exact key | [Optional] [Defaults to `undefined`] | +| **value** | `string` | Exact value | [Optional] [Defaults to `undefined`] | +| **q** | `string` | Key contains (case-insensitive) | [Optional] [Defaults to `undefined`] | + +### Return type + +[**Array<DtoLabelResponse>**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `application/json` + +### HTTP response details + +| Status code | Description | Response headers | +| ----------- | -------------------------- | ---------------- | +| **200** | OK | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **500** | failed to list node taints | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + +## updateLabel + +> DtoLabelResponse updateLabel(id, body, xOrgID) + +Update label (org scoped) + +Partially update label fields. + +### Example + +```ts +import { + Configuration, + LabelsApi, +} from '@glueops/autoglue-sdk'; +import type { UpdateLabelRequest } from '@glueops/autoglue-sdk'; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // string | Label ID (UUID) + id: id_example, + // DtoUpdateLabelRequest | Fields to update + body: ..., + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + } satisfies UpdateLabelRequest; + + try { + const data = await api.updateLabel(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + +| Name | Type | Description | Notes | +| ---------- | ------------------------------------------------- | ----------------- | ------------------------------------ | +| **id** | `string` | Label ID (UUID) | [Defaults to `undefined`] | +| **body** | [DtoUpdateLabelRequest](DtoUpdateLabelRequest.md) | Fields to update | | +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | + +### Return type + +[**DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: `application/json` +- **Accept**: `application/json` + +### HTTP response details + +| Status code | Description | Response headers | +| ----------- | ------------------------- | ---------------- | +| **200** | OK | - | +| **400** | invalid id / invalid json | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **404** | not found | - | +| **500** | update failed | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) diff --git a/sdk/ts/src/apis/LabelsApi.ts b/sdk/ts/src/apis/LabelsApi.ts new file mode 100644 index 0000000..39fb419 --- /dev/null +++ b/sdk/ts/src/apis/LabelsApi.ts @@ -0,0 +1,437 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * AutoGlue API + * API for managing K3s clusters across cloud providers + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import * as runtime from "../runtime"; +import type { + DtoCreateLabelRequest, + DtoLabelResponse, + DtoUpdateLabelRequest, +} from "../models/index"; +import { + DtoCreateLabelRequestFromJSON, + DtoCreateLabelRequestToJSON, + DtoLabelResponseFromJSON, + DtoLabelResponseToJSON, + DtoUpdateLabelRequestFromJSON, + DtoUpdateLabelRequestToJSON, +} from "../models/index"; + +export interface CreateLabelRequest { + body: DtoCreateLabelRequest; + xOrgID?: string; +} + +export interface DeleteLabelRequest { + id: string; + xOrgID?: string; +} + +export interface GetLabelRequest { + id: string; + xOrgID?: string; +} + +export interface ListLabelsRequest { + xOrgID?: string; + key?: string; + value?: string; + q?: string; +} + +export interface UpdateLabelRequest { + id: string; + body: DtoUpdateLabelRequest; + xOrgID?: string; +} + +/** + * + */ +export class LabelsApi extends runtime.BaseAPI { + /** + * Creates a label. + * Create label (org scoped) + */ + async createLabelRaw( + requestParameters: CreateLabelRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + if (requestParameters["body"] == null) { + throw new runtime.RequiredError( + "body", + 'Required parameter "body" was null or undefined when calling createLabel().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters["Content-Type"] = "application/json"; + + if (requestParameters["xOrgID"] != null) { + headerParameters["X-Org-ID"] = String(requestParameters["xOrgID"]); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = + await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = + await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = + await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + let urlPath = `/labels`; + + const response = await this.request( + { + path: urlPath, + method: "POST", + headers: headerParameters, + query: queryParameters, + body: DtoCreateLabelRequestToJSON(requestParameters["body"]), + }, + initOverrides, + ); + + return new runtime.JSONApiResponse(response, (jsonValue) => + DtoLabelResponseFromJSON(jsonValue), + ); + } + + /** + * Creates a label. + * Create label (org scoped) + */ + async createLabel( + requestParameters: CreateLabelRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.createLabelRaw( + requestParameters, + initOverrides, + ); + return await response.value(); + } + + /** + * Permanently deletes the label. + * Delete label (org scoped) + */ + async deleteLabelRaw( + requestParameters: DeleteLabelRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + if (requestParameters["id"] == null) { + throw new runtime.RequiredError( + "id", + 'Required parameter "id" was null or undefined when calling deleteLabel().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (requestParameters["xOrgID"] != null) { + headerParameters["X-Org-ID"] = String(requestParameters["xOrgID"]); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = + await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = + await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = + await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + let urlPath = `/labels/{id}`; + urlPath = urlPath.replace( + `{${"id"}}`, + encodeURIComponent(String(requestParameters["id"])), + ); + + const response = await this.request( + { + path: urlPath, + method: "DELETE", + headers: headerParameters, + query: queryParameters, + }, + initOverrides, + ); + + if (this.isJsonMime(response.headers.get("content-type"))) { + return new runtime.JSONApiResponse(response); + } else { + return new runtime.TextApiResponse(response) as any; + } + } + + /** + * Permanently deletes the label. + * Delete label (org scoped) + */ + async deleteLabel( + requestParameters: DeleteLabelRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.deleteLabelRaw( + requestParameters, + initOverrides, + ); + return await response.value(); + } + + /** + * Returns one label. + * Get label by ID (org scoped) + */ + async getLabelRaw( + requestParameters: GetLabelRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + if (requestParameters["id"] == null) { + throw new runtime.RequiredError( + "id", + 'Required parameter "id" was null or undefined when calling getLabel().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (requestParameters["xOrgID"] != null) { + headerParameters["X-Org-ID"] = String(requestParameters["xOrgID"]); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = + await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = + await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = + await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + let urlPath = `/labels/{id}`; + urlPath = urlPath.replace( + `{${"id"}}`, + encodeURIComponent(String(requestParameters["id"])), + ); + + const response = await this.request( + { + path: urlPath, + method: "GET", + headers: headerParameters, + query: queryParameters, + }, + initOverrides, + ); + + return new runtime.JSONApiResponse(response, (jsonValue) => + DtoLabelResponseFromJSON(jsonValue), + ); + } + + /** + * Returns one label. + * Get label by ID (org scoped) + */ + async getLabel( + requestParameters: GetLabelRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.getLabelRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups. + * List node labels (org scoped) + */ + async listLabelsRaw( + requestParameters: ListLabelsRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>> { + const queryParameters: any = {}; + + if (requestParameters["key"] != null) { + queryParameters["key"] = requestParameters["key"]; + } + + if (requestParameters["value"] != null) { + queryParameters["value"] = requestParameters["value"]; + } + + if (requestParameters["q"] != null) { + queryParameters["q"] = requestParameters["q"]; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (requestParameters["xOrgID"] != null) { + headerParameters["X-Org-ID"] = String(requestParameters["xOrgID"]); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = + await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = + await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = + await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + let urlPath = `/labels`; + + const response = await this.request( + { + path: urlPath, + method: "GET", + headers: headerParameters, + query: queryParameters, + }, + initOverrides, + ); + + return new runtime.JSONApiResponse(response, (jsonValue) => + jsonValue.map(DtoLabelResponseFromJSON), + ); + } + + /** + * Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups. + * List node labels (org scoped) + */ + async listLabels( + requestParameters: ListLabelsRequest = {}, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const response = await this.listLabelsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Partially update label fields. + * Update label (org scoped) + */ + async updateLabelRaw( + requestParameters: UpdateLabelRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + if (requestParameters["id"] == null) { + throw new runtime.RequiredError( + "id", + 'Required parameter "id" was null or undefined when calling updateLabel().', + ); + } + + if (requestParameters["body"] == null) { + throw new runtime.RequiredError( + "body", + 'Required parameter "body" was null or undefined when calling updateLabel().', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters["Content-Type"] = "application/json"; + + if (requestParameters["xOrgID"] != null) { + headerParameters["X-Org-ID"] = String(requestParameters["xOrgID"]); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = + await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = + await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = + await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + let urlPath = `/labels/{id}`; + urlPath = urlPath.replace( + `{${"id"}}`, + encodeURIComponent(String(requestParameters["id"])), + ); + + const response = await this.request( + { + path: urlPath, + method: "PATCH", + headers: headerParameters, + query: queryParameters, + body: DtoUpdateLabelRequestToJSON(requestParameters["body"]), + }, + initOverrides, + ); + + return new runtime.JSONApiResponse(response, (jsonValue) => + DtoLabelResponseFromJSON(jsonValue), + ); + } + + /** + * Partially update label fields. + * Update label (org scoped) + */ + async updateLabel( + requestParameters: UpdateLabelRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.updateLabelRaw( + requestParameters, + initOverrides, + ); + return await response.value(); + } +} diff --git a/sdk/ts/src/apis/index.ts b/sdk/ts/src/apis/index.ts index 9f8eb0e..1b2b3a3 100644 --- a/sdk/ts/src/apis/index.ts +++ b/sdk/ts/src/apis/index.ts @@ -1,6 +1,7 @@ /* tslint:disable */ /* eslint-disable */ export * from "./AuthApi"; +export * from "./LabelsApi"; export * from "./MeApi"; export * from "./MeAPIKeysApi"; export * from "./OrgsApi"; diff --git a/sdk/ts/src/models/DtoCreateLabelRequest.ts b/sdk/ts/src/models/DtoCreateLabelRequest.ts new file mode 100644 index 0000000..d6af7bd --- /dev/null +++ b/sdk/ts/src/models/DtoCreateLabelRequest.ts @@ -0,0 +1,80 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * AutoGlue API + * API for managing K3s clusters across cloud providers + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from "../runtime"; +/** + * + * @export + * @interface DtoCreateLabelRequest + */ +export interface DtoCreateLabelRequest { + /** + * + * @type {string} + * @memberof DtoCreateLabelRequest + */ + key?: string; + /** + * + * @type {string} + * @memberof DtoCreateLabelRequest + */ + value?: string; +} + +/** + * Check if a given object implements the DtoCreateLabelRequest interface. + */ +export function instanceOfDtoCreateLabelRequest( + value: object, +): value is DtoCreateLabelRequest { + return true; +} + +export function DtoCreateLabelRequestFromJSON( + json: any, +): DtoCreateLabelRequest { + return DtoCreateLabelRequestFromJSONTyped(json, false); +} + +export function DtoCreateLabelRequestFromJSONTyped( + json: any, + ignoreDiscriminator: boolean, +): DtoCreateLabelRequest { + if (json == null) { + return json; + } + return { + key: json["key"] == null ? undefined : json["key"], + value: json["value"] == null ? undefined : json["value"], + }; +} + +export function DtoCreateLabelRequestToJSON(json: any): DtoCreateLabelRequest { + return DtoCreateLabelRequestToJSONTyped(json, false); +} + +export function DtoCreateLabelRequestToJSONTyped( + value?: DtoCreateLabelRequest | null, + ignoreDiscriminator: boolean = false, +): any { + if (value == null) { + return value; + } + + return { + key: value["key"], + value: value["value"], + }; +} diff --git a/sdk/ts/src/models/DtoLabelResponse.ts b/sdk/ts/src/models/DtoLabelResponse.ts new file mode 100644 index 0000000..6188a57 --- /dev/null +++ b/sdk/ts/src/models/DtoLabelResponse.ts @@ -0,0 +1,86 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * AutoGlue API + * API for managing K3s clusters across cloud providers + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from "../runtime"; +/** + * + * @export + * @interface DtoLabelResponse + */ +export interface DtoLabelResponse { + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + id?: string; + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + key?: string; + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + value?: string; +} + +/** + * Check if a given object implements the DtoLabelResponse interface. + */ +export function instanceOfDtoLabelResponse( + value: object, +): value is DtoLabelResponse { + return true; +} + +export function DtoLabelResponseFromJSON(json: any): DtoLabelResponse { + return DtoLabelResponseFromJSONTyped(json, false); +} + +export function DtoLabelResponseFromJSONTyped( + json: any, + ignoreDiscriminator: boolean, +): DtoLabelResponse { + if (json == null) { + return json; + } + return { + id: json["id"] == null ? undefined : json["id"], + key: json["key"] == null ? undefined : json["key"], + value: json["value"] == null ? undefined : json["value"], + }; +} + +export function DtoLabelResponseToJSON(json: any): DtoLabelResponse { + return DtoLabelResponseToJSONTyped(json, false); +} + +export function DtoLabelResponseToJSONTyped( + value?: DtoLabelResponse | null, + ignoreDiscriminator: boolean = false, +): any { + if (value == null) { + return value; + } + + return { + id: value["id"], + key: value["key"], + value: value["value"], + }; +} diff --git a/sdk/ts/src/models/DtoUpdateLabelRequest.ts b/sdk/ts/src/models/DtoUpdateLabelRequest.ts new file mode 100644 index 0000000..a838fb6 --- /dev/null +++ b/sdk/ts/src/models/DtoUpdateLabelRequest.ts @@ -0,0 +1,80 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * AutoGlue API + * API for managing K3s clusters across cloud providers + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from "../runtime"; +/** + * + * @export + * @interface DtoUpdateLabelRequest + */ +export interface DtoUpdateLabelRequest { + /** + * + * @type {string} + * @memberof DtoUpdateLabelRequest + */ + key?: string; + /** + * + * @type {string} + * @memberof DtoUpdateLabelRequest + */ + value?: string; +} + +/** + * Check if a given object implements the DtoUpdateLabelRequest interface. + */ +export function instanceOfDtoUpdateLabelRequest( + value: object, +): value is DtoUpdateLabelRequest { + return true; +} + +export function DtoUpdateLabelRequestFromJSON( + json: any, +): DtoUpdateLabelRequest { + return DtoUpdateLabelRequestFromJSONTyped(json, false); +} + +export function DtoUpdateLabelRequestFromJSONTyped( + json: any, + ignoreDiscriminator: boolean, +): DtoUpdateLabelRequest { + if (json == null) { + return json; + } + return { + key: json["key"] == null ? undefined : json["key"], + value: json["value"] == null ? undefined : json["value"], + }; +} + +export function DtoUpdateLabelRequestToJSON(json: any): DtoUpdateLabelRequest { + return DtoUpdateLabelRequestToJSONTyped(json, false); +} + +export function DtoUpdateLabelRequestToJSONTyped( + value?: DtoUpdateLabelRequest | null, + ignoreDiscriminator: boolean = false, +): any { + if (value == null) { + return value; + } + + return { + key: value["key"], + value: value["value"], + }; +} diff --git a/sdk/ts/src/models/index.ts b/sdk/ts/src/models/index.ts index 1f95a32..e64b6a5 100644 --- a/sdk/ts/src/models/index.ts +++ b/sdk/ts/src/models/index.ts @@ -1,11 +1,13 @@ /* tslint:disable */ /* eslint-disable */ export * from "./DtoAuthStartResponse"; +export * from "./DtoCreateLabelRequest"; export * from "./DtoCreateSSHRequest"; export * from "./DtoCreateServerRequest"; export * from "./DtoCreateTaintRequest"; export * from "./DtoJWK"; export * from "./DtoJWKS"; +export * from "./DtoLabelResponse"; export * from "./DtoLogoutRequest"; export * from "./DtoRefreshRequest"; export * from "./DtoServerResponse"; @@ -13,6 +15,7 @@ export * from "./DtoSshResponse"; export * from "./DtoSshRevealResponse"; export * from "./DtoTaintResponse"; export * from "./DtoTokenPair"; +export * from "./DtoUpdateLabelRequest"; export * from "./DtoUpdateServerRequest"; export * from "./DtoUpdateTaintRequest"; export * from "./HandlersCreateUserKeyRequest"; diff --git a/terraform-provider-autoglue/internal/provider/datasource_server.go b/terraform-provider-autoglue/internal/provider/datasource_server.go new file mode 100644 index 0000000..b28c015 --- /dev/null +++ b/terraform-provider-autoglue/internal/provider/datasource_server.go @@ -0,0 +1,149 @@ +package provider + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + dschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ datasource.DataSource = &ServersDataSource{} +var _ datasource.DataSourceWithConfigure = &ServersDataSource{} + +type ServersDataSource struct{ client *Client } + +func NewServersDataSource() datasource.DataSource { return &ServersDataSource{} } + +type serversDSModel struct { + Status types.String `tfsdk:"status"` // pending|provisioning|ready|failed + Role types.String `tfsdk:"role"` + Items []serverItem `tfsdk:"items"` +} + +type serverItem struct { + // IDs & timestamps + ID types.String `tfsdk:"id"` + OrganizationID types.String `tfsdk:"organization_id"` + CreatedAt types.String `tfsdk:"created_at"` + UpdatedAt types.String `tfsdk:"updated_at"` + + // Desired/actual fields (DTO) + Hostname types.String `tfsdk:"hostname"` + PrivateIPAddress types.String `tfsdk:"private_ip_address"` + PublicIPAddress types.String `tfsdk:"public_ip_address"` + Role types.String `tfsdk:"role"` + SSHKeyID types.String `tfsdk:"ssh_key_id"` + SSHUser types.String `tfsdk:"ssh_user"` + Status types.String `tfsdk:"status"` + + // Raw JSON payload from API for debugging + Raw types.String `tfsdk:"raw"` +} + +func (d *ServersDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_servers" +} + +func (d *ServersDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = dschema.Schema{ + Description: "List servers for the organization (org-scoped).", + Attributes: map[string]dschema.Attribute{ + "status": dschema.StringAttribute{ + Optional: true, + Description: "Filter by status (pending|provisioning|ready|failed).", + Validators: []validator.String{ + stringvalidator.OneOf("pending", "provisioning", "ready", "failed"), + }, + }, + "role": dschema.StringAttribute{ + Optional: true, + Description: "Filter by role.", + }, + "items": dschema.ListNestedAttribute{ + Computed: true, + Description: "Servers returned by the API.", + NestedObject: dschema.NestedAttributeObject{ + Attributes: map[string]dschema.Attribute{ + "id": dschema.StringAttribute{Computed: true, Description: "Server ID (UUID)."}, + "organization_id": dschema.StringAttribute{Computed: true}, + "hostname": dschema.StringAttribute{Computed: true}, + "private_ip_address": dschema.StringAttribute{Computed: true}, + "public_ip_address": dschema.StringAttribute{Computed: true}, + "role": dschema.StringAttribute{Computed: true}, + "ssh_key_id": dschema.StringAttribute{Computed: true}, + "ssh_user": dschema.StringAttribute{Computed: true}, + "status": dschema.StringAttribute{Computed: true}, + "created_at": dschema.StringAttribute{Computed: true, Description: "RFC3339, UTC."}, + "updated_at": dschema.StringAttribute{Computed: true, Description: "RFC3339, UTC."}, + "raw": dschema.StringAttribute{Computed: true, Description: "Full JSON for the item."}, + }, + }, + }, + }, + } +} + +func (d *ServersDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + d.client = req.ProviderData.(*Client) +} + +func (d *ServersDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + if d.client == nil || d.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var conf serversDSModel + resp.Diagnostics.Append(req.Config.Get(ctx, &conf)...) + if resp.Diagnostics.HasError() { + return + } + + call := d.client.SDK.ServersAPI.ListServers(ctx) + if !conf.Status.IsNull() && !conf.Status.IsUnknown() { + call = call.Status(conf.Status.ValueString()) + } + if !conf.Role.IsNull() && !conf.Role.IsUnknown() { + call = call.Role(conf.Role.ValueString()) + } + + items, httpResp, err := call.Execute() + if err != nil { + resp.Diagnostics.AddError("List servers failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + return + } + + out := serversDSModel{ + Status: conf.Status, + Role: conf.Role, + Items: make([]serverItem, 0, len(items)), + } + + for _, s := range items { + raw, _ := json.Marshal(s) + out.Items = append(out.Items, serverItem{ + ID: types.StringPointerValue(s.Id), + OrganizationID: types.StringPointerValue(s.OrganizationId), + Hostname: types.StringPointerValue(s.Hostname), + PrivateIPAddress: types.StringPointerValue(s.PrivateIpAddress), + PublicIPAddress: types.StringPointerValue(s.PublicIpAddress), + Role: types.StringPointerValue(s.Role), + SSHKeyID: types.StringPointerValue(s.SshKeyId), + SSHUser: types.StringPointerValue(s.SshUser), + Status: types.StringPointerValue(s.Status), + CreatedAt: types.StringPointerValue(s.CreatedAt), + UpdatedAt: types.StringPointerValue(s.UpdatedAt), + Raw: types.StringValue(string(raw)), + }) + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &out)...) +} diff --git a/terraform-provider-autoglue/internal/provider/provider.go b/terraform-provider-autoglue/internal/provider/provider.go index 4d659f0..bfb1e2b 100644 --- a/terraform-provider-autoglue/internal/provider/provider.go +++ b/terraform-provider-autoglue/internal/provider/provider.go @@ -48,11 +48,13 @@ func (p *AutoglueProvider) Configure(ctx context.Context, req provider.Configure func (p *AutoglueProvider) DataSources(_ context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ NewSshDataSource, + NewServersDataSource, } } func (p *AutoglueProvider) Resources(_ context.Context) []func() resource.Resource { return []func() resource.Resource{ NewSshResource, + NewServerResource, } } diff --git a/terraform-provider-autoglue/internal/provider/resource_server.go b/terraform-provider-autoglue/internal/provider/resource_server.go new file mode 100644 index 0000000..d7fa185 --- /dev/null +++ b/terraform-provider-autoglue/internal/provider/resource_server.go @@ -0,0 +1,382 @@ +package provider + +import ( + "context" + "encoding/json" + "fmt" + "regexp" + "strings" + + "github.com/glueops/autoglue-sdk" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ resource.Resource = &ServerResource{} +var _ resource.ResourceWithConfigure = &ServerResource{} +var _ resource.ResourceWithImportState = &ServerResource{} + +type ServerResource struct{ client *Client } + +func NewServerResource() resource.Resource { return &ServerResource{} } + +type serverResModel struct { + // Identity + ID types.String `tfsdk:"id"` + OrganizationID types.String `tfsdk:"organization_id"` + CreatedAt types.String `tfsdk:"created_at"` + UpdatedAt types.String `tfsdk:"updated_at"` + + // DTO fields + Hostname types.String `tfsdk:"hostname"` + PrivateIPAddress types.String `tfsdk:"private_ip_address"` + PublicIPAddress types.String `tfsdk:"public_ip_address"` + Role types.String `tfsdk:"role"` + SSHKeyID types.String `tfsdk:"ssh_key_id"` + SSHUser types.String `tfsdk:"ssh_user"` + Status types.String `tfsdk:"status"` + + // Raw JSON for debugging + Raw types.String `tfsdk:"raw"` +} + +func (r *ServerResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_server" +} + +var uuidRx = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$`) + +func (r *ServerResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = rschema.Schema{ + Description: "Create and manage a server (org-scoped). Mirrors API validation for role/status/ssh_key_id.", + Attributes: map[string]rschema.Attribute{ + "id": rschema.StringAttribute{ + Computed: true, + Description: "Server ID (UUID).", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "organization_id": rschema.StringAttribute{Computed: true}, + "created_at": rschema.StringAttribute{Computed: true}, + "updated_at": rschema.StringAttribute{Computed: true}, + + "hostname": rschema.StringAttribute{ + Required: true, + Description: "Hostname.", + }, + "private_ip_address": rschema.StringAttribute{ + Required: true, // API requires on create + Description: "Private IP address (required).", + }, + "public_ip_address": rschema.StringAttribute{ + Optional: true, // required only if role=bastion + Description: "Public IP address (required when role = bastion).", + }, + "role": rschema.StringAttribute{ + Required: true, // API requires on create + Description: "Server role (e.g., agent/manager/bastion). Lowercased by the provider.", + }, + "ssh_key_id": rschema.StringAttribute{ + Required: true, // API requires on create + Description: "SSH key ID (UUID) that belongs to the org.", + Validators: []validator.String{ + stringvalidator.RegexMatches(uuidRx, "must be a valid UUID"), + }, + }, + "ssh_user": rschema.StringAttribute{ + Required: true, // API requires on create + Description: "SSH username (required).", + }, + "status": rschema.StringAttribute{ + Optional: true, // patchable; if omitted, server sets/returns it + Computed: true, + Description: "Status (pending|provisioning|ready|failed). Lowercased by the provider.", + Validators: []validator.String{ + stringvalidator.OneOf("", "pending", "provisioning", "ready", "failed"), + }, + }, + "raw": rschema.StringAttribute{ + Computed: true, + Description: "Full server JSON from API.", + }, + }, + } +} + +func (r *ServerResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + r.client = req.ProviderData.(*Client) +} + +func (r *ServerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + if r.client == nil || r.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var plan serverResModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + // Normalize + validate against backend rules + role := strings.ToLower(strings.TrimSpace(plan.Role.ValueString())) + status := strings.ToLower(strings.TrimSpace(plan.Status.ValueString())) + pub := strings.TrimSpace(plan.PublicIPAddress.ValueString()) + + if role == "bastion" && pub == "" { + resp.Diagnostics.AddAttributeError( + path.Root("public_ip_address"), + "Public IP required for bastion", + "public_ip_address must be set when role is 'bastion'.", + ) + return + } + + body := autoglue.DtoCreateServerRequest{ + Hostname: stringPtrFromAttr(plan.Hostname), + PrivateIpAddress: stringPtrFromAttr(plan.PrivateIPAddress), + PublicIpAddress: nil, + Role: &role, + SshKeyId: stringPtrFromAttr(plan.SSHKeyID), + SshUser: stringPtrFromAttr(plan.SSHUser), + } + if pub != "" { + body.PublicIpAddress = &pub + } + if status != "" { + body.Status = &status // validator already checked allowed values + } + + created, httpResp, err := r.client.SDK. + ServersAPI. + CreateServer(ctx). + Body(body). + Execute() + if err != nil { + resp.Diagnostics.AddError("Create server failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + return + } + + var state serverResModel + r.mapRespToState(created, &state) + raw, _ := json.Marshal(created) + state.Raw = types.StringValue(string(raw)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ServerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + if r.client == nil || r.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var state serverResModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + got, httpResp, err := r.client.SDK. + ServersAPI. + GetServer(ctx, state.ID.ValueString()). + Execute() + if err != nil { + if isNotFound(httpResp) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError("Read server failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + return + } + + r.mapRespToState(got, &state) + raw, _ := json.Marshal(got) + state.Raw = types.StringValue(string(raw)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ServerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + if r.client == nil || r.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var plan, state serverResModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + patch := autoglue.DtoUpdateServerRequest{} + + // helpers to set changed fields + setIfChanged := func(p types.String, s types.String, setter func(string)) { + if p.IsUnknown() || p.IsNull() { + return + } + if s.IsNull() || s.IsUnknown() || p.ValueString() != s.ValueString() { + setter(p.ValueString()) + } + } + + setIfChanged(plan.Hostname, state.Hostname, func(v string) { patch.Hostname = strPtr(v) }) + setIfChanged(plan.PrivateIPAddress, state.PrivateIPAddress, func(v string) { patch.PrivateIpAddress = strPtr(v) }) + setIfChanged(plan.PublicIPAddress, state.PublicIPAddress, func(v string) { patch.PublicIpAddress = strPtr(strings.TrimSpace(v)) }) + setIfChanged(plan.SSHUser, state.SSHUser, func(v string) { patch.SshUser = strPtr(v) }) + + // Normalize role/status and enforce rules + if !plan.Role.IsNull() && !plan.Role.IsUnknown() { + role := strings.ToLower(strings.TrimSpace(plan.Role.ValueString())) + if state.Role.IsNull() || state.Role.IsUnknown() || role != strings.ToLower(state.Role.ValueString()) { + patch.Role = &role + } + } + if !plan.Status.IsNull() && !plan.Status.IsUnknown() { + status := strings.ToLower(strings.TrimSpace(plan.Status.ValueString())) + patch.Status = &status + } + + // ssh_key_id: validate UUID via regex at runtime too (gives precise attribute error) + if !plan.SSHKeyID.IsNull() && !plan.SSHKeyID.IsUnknown() { + if !uuidRx.MatchString(plan.SSHKeyID.ValueString()) { + resp.Diagnostics.AddAttributeError( + path.Root("ssh_key_id"), + "Invalid ssh_key_id", + "ssh_key_id must be a valid UUID.", + ) + return + } + if state.SSHKeyID.IsNull() || state.SSHKeyID.IsUnknown() || plan.SSHKeyID.ValueString() != state.SSHKeyID.ValueString() { + patch.SshKeyId = strPtr(plan.SSHKeyID.ValueString()) + } + } + + // Bastion rule: if resulting role == "bastion" ensure resulting public IP is non-empty + resultRole := firstNonEmptyLower(plan.Role, state.Role) + resultPub := firstNonEmptyTrim(plan.PublicIPAddress, state.PublicIPAddress) + if resultRole == "bastion" && resultPub == "" { + resp.Diagnostics.AddAttributeError( + path.Root("public_ip_address"), + "Public IP required for bastion", + "public_ip_address must be set when role is 'bastion'.", + ) + return + } + + if isEmptyUpdateServerRequest(patch) { + // Nothing to do; persist state unchanged. + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + return + } + + updated, httpResp, err := r.client.SDK. + ServersAPI. + UpdateServer(ctx, state.ID.ValueString()). + Body(patch). + Execute() + if err != nil { + resp.Diagnostics.AddError("Update server failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + return + } + + r.mapRespToState(updated, &state) + raw, _ := json.Marshal(updated) + state.Raw = types.StringValue(string(raw)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ServerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + if r.client == nil || r.client.SDK == nil { + resp.Diagnostics.AddError("Client not configured", "Provider configuration missing") + return + } + + var state serverResModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + _, httpResp, err := r.client.SDK. + ServersAPI. + DeleteServer(ctx, state.ID.ValueString()). + Execute() + if err != nil && !isNotFound(httpResp) { + resp.Diagnostics.AddError("Delete server failed", fmt.Sprintf("%v", httpErr(err, httpResp))) + } +} + +func (r *ServerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), req.ID)...) +} + +// --- helpers --- + +func (r *ServerResource) mapRespToState(s *autoglue.DtoServerResponse, out *serverResModel) { + out.ID = types.StringPointerValue(s.Id) + out.OrganizationID = types.StringPointerValue(s.OrganizationId) + out.Hostname = types.StringPointerValue(s.Hostname) + out.PrivateIPAddress = types.StringPointerValue(s.PrivateIpAddress) + out.PublicIPAddress = types.StringPointerValue(s.PublicIpAddress) + out.Role = types.StringPointerValue(s.Role) + out.SSHKeyID = types.StringPointerValue(s.SshKeyId) + out.SSHUser = types.StringPointerValue(s.SshUser) + out.Status = types.StringPointerValue(s.Status) + out.CreatedAt = types.StringPointerValue(s.CreatedAt) + out.UpdatedAt = types.StringPointerValue(s.UpdatedAt) +} + +func stringPtrFromAttr(a types.String) *string { + if a.IsNull() || a.IsUnknown() { + return nil + } + v := a.ValueString() + return &v +} + +func strPtr(v string) *string { return &v } + +func isEmptyUpdateServerRequest(u autoglue.DtoUpdateServerRequest) bool { + return u.Hostname == nil && + u.PrivateIpAddress == nil && + u.PublicIpAddress == nil && + u.Role == nil && + u.SshKeyId == nil && + u.SshUser == nil && + u.Status == nil +} + +func firstNonEmptyLower(a, b types.String) string { + if !a.IsNull() && !a.IsUnknown() && strings.TrimSpace(a.ValueString()) != "" { + return strings.ToLower(strings.TrimSpace(a.ValueString())) + } + if !b.IsNull() && !b.IsUnknown() { + return strings.ToLower(strings.TrimSpace(b.ValueString())) + } + return "" +} + +func firstNonEmptyTrim(a, b types.String) string { + if !a.IsNull() && !a.IsUnknown() && strings.TrimSpace(a.ValueString()) != "" { + return strings.TrimSpace(a.ValueString()) + } + if !b.IsNull() && !b.IsUnknown() { + return strings.TrimSpace(b.ValueString()) + } + return "" +} diff --git a/terraform/envs/dev/.terraform.lock.hcl b/terraform/envs/dev/.terraform.lock.hcl index 68d22e7..f84d0c6 100644 --- a/terraform/envs/dev/.terraform.lock.hcl +++ b/terraform/envs/dev/.terraform.lock.hcl @@ -5,7 +5,7 @@ provider "glueops/autoglue/autoglue" { version = "0.0.1" constraints = "0.0.1" hashes = [ - "h1:XW1zYWB6NTuE7jgJwWAkZeBBhL3Me36KE4Puy6lN6+o=", + "h1:K5xMCf5zxZVCurwzkSEAaMv70dzBlVU8VN/q72sNyD0=", ] } diff --git a/terraform/envs/dev/.terraform/modules/modules.json b/terraform/envs/dev/.terraform/modules/modules.json index 8096847..e8f9011 100644 --- a/terraform/envs/dev/.terraform/modules/modules.json +++ b/terraform/envs/dev/.terraform/modules/modules.json @@ -1 +1 @@ -{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"ssh","Source":"../../modules/ssh-key","Dir":"../../modules/ssh-key"}]} \ No newline at end of file +{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"servers","Source":"../../modules/servers","Dir":"../../modules/servers"},{"Key":"ssh","Source":"../../modules/ssh-key","Dir":"../../modules/ssh-key"}]} \ No newline at end of file diff --git a/terraform/envs/dev/main.tf b/terraform/envs/dev/main.tf index 07c62a1..bd6f279 100644 --- a/terraform/envs/dev/main.tf +++ b/terraform/envs/dev/main.tf @@ -27,3 +27,47 @@ output "ssh_public_keys" { output "ssh_written_files" { value = { for k, m in module.ssh : k => m.written_files } } + +module "servers" { + source = "../../modules/servers" + # Wire the SSH key IDs so servers can reference them by name + ssh_key_ids = { for k, m in module.ssh : k => m.id } + + servers = { + bastion = { + hostname = "bastion-01" + private_ip_address = "10.0.0.10" + public_ip_address = "54.12.34.56" # required for role=bastion + role = "bastion" + ssh_user = "ubuntu" + ssh_key_ref = "bastionKey" # points to module.ssh["bastionKey"].id + status = "pending" + } + + manager1 = { + hostname = "k3s-mgr-01" + private_ip_address = "10.0.1.11" + role = "manager" + ssh_user = "ubuntu" + ssh_key_ref = "clusterKey" + status = "pending" + } + + agent1 = { + hostname = "k3s-agent-01" + private_ip_address = "10.0.2.21" + role = "agent" + ssh_user = "ubuntu" + ssh_key_ref = "clusterKey" + status = "pending" + } + } +} + +output "server_ids" { + value = module.servers.ids +} + +output "server_statuses" { + value = module.servers.statuses +} \ No newline at end of file diff --git a/terraform/envs/dev/terraform.tfstate b/terraform/envs/dev/terraform.tfstate index 4d2eda5..6143c63 100644 --- a/terraform/envs/dev/terraform.tfstate +++ b/terraform/envs/dev/terraform.tfstate @@ -1 +1 @@ -{"version":4,"terraform_version":"1.10.6","serial":2,"lineage":"6ea5f588-9a03-6b57-9ea8-06e8e7fc2461","outputs":{},"resources":[],"check_results":[{"object_kind":"resource","config_addr":"module.ssh.local_sensitive_file.zip","status":"unknown","objects":null}]} +{"version":4,"terraform_version":"1.10.6","serial":2,"lineage":"48e22363-3129-407c-1198-34f8c4d9e309","outputs":{"server_ids":{"value":{"agent1":"4a27b878-0049-4c39-891b-a8962ad17e8a","bastion":"265f6544-8e64-4e58-a950-7749df6d0a0d","manager1":"6e81d5d1-494d-4ec0-a62f-5a0e4293751f"},"type":["object",{"agent1":"string","bastion":"string","manager1":"string"}]},"server_statuses":{"value":{"agent1":"pending","bastion":"pending","manager1":"pending"},"type":["object",{"agent1":"string","bastion":"string","manager1":"string"}]},"ssh_ids":{"value":{"bastionKey":"9f5703f5-d518-462c-bd46-80cd8d5cd341","clusterKey":"7898e3cc-3032-4c73-bd0d-9834916276a4"},"type":["object",{"bastionKey":"string","clusterKey":"string"}]},"ssh_public_keys":{"value":{"bastionKey":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDKG889JECnruXYKEw8XTzz4C+0rQhUqOiHTc8JvFtYgk6yOIXRax7f3mUyxV4+FTwljSgyayEjFtwumFNVAg47hFry4NKFClTR+pZckDgLN938Hsk7lNExX4vfwQwVoIWzLTGpOK9Al0Lv+9TKUCpkWJdP0o8ZnNdrnrtj3xhukul7myYEOfppajmgDgchCCUTKX/o8kkrPm91hu3y2v8qzmvVdXok143zvH8KPBYpHZ1xu/wcrl0stbH+H1eO++V2fdSX/286bQBSU51p2FeTaidU3WOymh6DOcVtLuEPHQZF/rBx4BM9MTZr37X7qUTWRDgLRj0M4JVCPSsFWW0yQ6kL5gDcDDpgfGPCR4G8aEhD5LTOFw5Hg6tUxa/juplV9nO0E1SPIeb7ni1bCIHtUAJL7kb+CGERK/3H3Fy+GUO67ULJCjIzLkBSK9d5bQOhkWj+bCkYBZS6C+ivrCmKFlrP47YYqFGu6O+qaAT3RA2XmWLlgQ2OXxXTTQFG/sJw+wsm6Q9DoaLzbMDQKR5LbMVVFHiEapJbq5ZFBFQ/KswuQvy3/UpPQMzhGXhYHdEueVMsSk/46W3UHw7QJdbs2E/ADr9wGPixoREfwS9cETw8iV3nCq5lcCV9gVNbHU+IYGZas9XxHFim85ttVoHXePx0WznCJuxlNY93l1n1w== deploy@autoglue","clusterKey":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILitBBXEK8nbLSE9prC0pAwLbRTc5QztLMO6NsijwTZC bastion@autoglue"},"type":["object",{"bastionKey":"string","clusterKey":"string"}]},"ssh_written_files":{"value":{"bastionKey":["out/bastionKey/id_rsa_9f5703f5-d518-462c-bd46-80cd8d5cd341.zip"],"clusterKey":["out/clusterKey/id_ed25519_7898e3cc-3032-4c73-bd0d-9834916276a4.zip"]},"type":["object",{"bastionKey":["list","string"],"clusterKey":["list","string"]}]}},"resources":[{"module":"module.servers","mode":"managed","type":"autoglue_server","name":"this","provider":"provider[\"glueops/autoglue/autoglue\"]","instances":[{"index_key":"agent1","schema_version":0,"attributes":{"created_at":"2025-11-02T17:16:13.650415Z","hostname":"k3s-agent-01","id":"4a27b878-0049-4c39-891b-a8962ad17e8a","organization_id":"11a40e2e-fff9-49ff-84cf-60f6aff488ed","private_ip_address":"10.0.2.21","public_ip_address":null,"raw":"{\"created_at\":\"2025-11-02T17:16:13.650415Z\",\"hostname\":\"k3s-agent-01\",\"id\":\"4a27b878-0049-4c39-891b-a8962ad17e8a\",\"organization_id\":\"11a40e2e-fff9-49ff-84cf-60f6aff488ed\",\"private_ip_address\":\"10.0.2.21\",\"role\":\"agent\",\"ssh_key_id\":\"7898e3cc-3032-4c73-bd0d-9834916276a4\",\"ssh_user\":\"ubuntu\",\"status\":\"pending\",\"updated_at\":\"2025-11-02T17:16:13.650415Z\"}","role":"agent","ssh_key_id":"7898e3cc-3032-4c73-bd0d-9834916276a4","ssh_user":"ubuntu","status":"pending","updated_at":"2025-11-02T17:16:13.650415Z"},"sensitive_attributes":[],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.local_file.public_key","module.ssh.local_sensitive_file.private_key","module.ssh.local_sensitive_file.zip","module.ssh.null_resource.mkdirs"]},{"index_key":"bastion","schema_version":0,"attributes":{"created_at":"2025-11-02T17:16:13.636733Z","hostname":"bastion-01","id":"265f6544-8e64-4e58-a950-7749df6d0a0d","organization_id":"11a40e2e-fff9-49ff-84cf-60f6aff488ed","private_ip_address":"10.0.0.10","public_ip_address":"54.12.34.56","raw":"{\"created_at\":\"2025-11-02T17:16:13.636733Z\",\"hostname\":\"bastion-01\",\"id\":\"265f6544-8e64-4e58-a950-7749df6d0a0d\",\"organization_id\":\"11a40e2e-fff9-49ff-84cf-60f6aff488ed\",\"private_ip_address\":\"10.0.0.10\",\"public_ip_address\":\"54.12.34.56\",\"role\":\"bastion\",\"ssh_key_id\":\"9f5703f5-d518-462c-bd46-80cd8d5cd341\",\"ssh_user\":\"ubuntu\",\"status\":\"pending\",\"updated_at\":\"2025-11-02T17:16:13.636733Z\"}","role":"bastion","ssh_key_id":"9f5703f5-d518-462c-bd46-80cd8d5cd341","ssh_user":"ubuntu","status":"pending","updated_at":"2025-11-02T17:16:13.636733Z"},"sensitive_attributes":[],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.local_file.public_key","module.ssh.local_sensitive_file.private_key","module.ssh.local_sensitive_file.zip","module.ssh.null_resource.mkdirs"]},{"index_key":"manager1","schema_version":0,"attributes":{"created_at":"2025-11-02T17:16:13.636508Z","hostname":"k3s-mgr-01","id":"6e81d5d1-494d-4ec0-a62f-5a0e4293751f","organization_id":"11a40e2e-fff9-49ff-84cf-60f6aff488ed","private_ip_address":"10.0.1.11","public_ip_address":null,"raw":"{\"created_at\":\"2025-11-02T17:16:13.636508Z\",\"hostname\":\"k3s-mgr-01\",\"id\":\"6e81d5d1-494d-4ec0-a62f-5a0e4293751f\",\"organization_id\":\"11a40e2e-fff9-49ff-84cf-60f6aff488ed\",\"private_ip_address\":\"10.0.1.11\",\"role\":\"manager\",\"ssh_key_id\":\"7898e3cc-3032-4c73-bd0d-9834916276a4\",\"ssh_user\":\"ubuntu\",\"status\":\"pending\",\"updated_at\":\"2025-11-02T17:16:13.636508Z\"}","role":"manager","ssh_key_id":"7898e3cc-3032-4c73-bd0d-9834916276a4","ssh_user":"ubuntu","status":"pending","updated_at":"2025-11-02T17:16:13.636508Z"},"sensitive_attributes":[],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.local_file.public_key","module.ssh.local_sensitive_file.private_key","module.ssh.local_sensitive_file.zip","module.ssh.null_resource.mkdirs"]}]},{"module":"module.ssh[\"bastionKey\"]","mode":"data","type":"http","name":"download","provider":"provider[\"registry.opentofu.org/hashicorp/http\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"body":"{\"id\":\"9f5703f5-d518-462c-bd46-80cd8d5cd341\",\"name\":\"Bastion Key\",\"fingerprint\":\"SHA256:ksju4vKj/QAEo8pfmkmPUXUbXrMIG/KqG/TlV9gQtZc\",\"zip_base64\":\"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\",\"filenames\":[\"id_rsa_9f5703f5-d518-462c-bd46-80cd8d5cd341.zip\",\"id_rsa_9f5703f5-d518-462c-bd46-80cd8d5cd341.pem\",\"id_rsa_9f5703f5-d518-462c-bd46-80cd8d5cd341.pub\"]}\n","ca_cert_pem":null,"client_cert_pem":null,"client_key_pem":null,"id":"http://localhost:8080/api/v1/ssh/9f5703f5-d518-462c-bd46-80cd8d5cd341/download?part=both\u0026mode=json","insecure":null,"method":null,"request_body":null,"request_headers":{"Accept":"application/json","X-ORG-KEY":"org_lnJwmyyWH7JC-JgZo5v3Kw","X-ORG-SECRET":"fqd9yebGMfK6h5HSgWn4sXrwr9xlFbvbIYtNylRElMQ"},"request_timeout_ms":null,"response_body":"{\"id\":\"9f5703f5-d518-462c-bd46-80cd8d5cd341\",\"name\":\"Bastion Key\",\"fingerprint\":\"SHA256:ksju4vKj/QAEo8pfmkmPUXUbXrMIG/KqG/TlV9gQtZc\",\"zip_base64\":\"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\",\"filenames\":[\"id_rsa_9f5703f5-d518-462c-bd46-80cd8d5cd341.zip\",\"id_rsa_9f5703f5-d518-462c-bd46-80cd8d5cd341.pem\",\"id_rsa_9f5703f5-d518-462c-bd46-80cd8d5cd341.pub\"]}\n","response_body_base64":"eyJpZCI6IjlmNTcwM2Y1LWQ1MTgtNDYyYy1iZDQ2LTgwY2Q4ZDVjZDM0MSIsIm5hbWUiOiJCYXN0aW9uIEtleSIsImZpbmdlcnByaW50IjoiU0hBMjU2OmtzanU0dktqL1FBRW84cGZta21QVVhVYlhyTUlHL0txRy9UbFY5Z1F0WmMiLCJ6aXBfYmFzZTY0IjoiVUVzREJCUUFDQUFJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF2QUFBQWFXUmZjbk5oWHpsbU5UY3dNMlkxTFdRMU1UZ3RORFl5WXkxaVpEUTJMVGd3WTJRNFpEVmpaRE0wTVM1d1pXMXNsN2NPcklvQkJYdSs0dlhJSXFmQ0JiRGtuQmM2Y2c1TGhxKzM3clZMbi9aMG8ybm1YMy9HQ1pKaS91TjY3RCsycTRTc0wveWpDZkhmQnpBVVJkVlloV05aaldkcmdiMmU1clJ0ejJVWGtocFF2akZzRWFacGtEMTNEVlpDclpzYkdMUDU3TTRpUmJXMnJnVkVPTnBLakNFR1l3dEx1d2xvTllablk5d2E1YzV5ZlduODBGRXNDM2RUK3ZoNGpiTUVjUFdMdmlxTDgyOVZRQndGcUJiaHUwWUk2aGd0R0dMS2k5c0QzeisvUFIyMTRDbm9ESjZCL1NWaSsvVVp6WmFTOTVzU0pCVXpkSnd0K2xJU0d6QnluNGxNbzVtWVdWeFduSG9RbmdFaWJWWFZYdUlzNG15bjl4WFNObUpGdmlHcG1xVkpVemZFZDFla3VXaVJaeWRBeXhwUjZIcDBRdzN6N2M1ektDWXNHQ0RtdEVhWURRYXJTSXZtMjZQekZKcEl0eTF6V2IrVG1IbFU4N2xoeWExSTRNcEwxcmQ5QVUwWkVBSEpRVUI2QitmaG1QbFlmT2gwOE1xRncyNzAxc0tETVl1eEZ4bkxkM2YxSlhkS2plS0FIZ0MvVFc2NXVMV0dSblRpMUM4SnErVGRPVFBvSG5rRUNYeVBycGFqUXBiM1FFS0M2cVlSdkpPNW1Ucy9tK0Z6SEFqQVA4NzhHQS9CQnUzNUxTUDRNeWZpREkzWElyRlJjTlRudkpMWDBqWkorb0lXS21tTnV4N2QrUnRyZ1lFZGN5QUdvQ1Zpd2V3R09vUmhtSE5QaXMvdFRWZVAzblpJcWFYUmw0WDV2dlRRVnd4ZEYyK2tueGVSUitRNmJtQS9xNkgzT2tEdkVLTDU4TWZBM1FHUE9NelZwOEhYaGNVR3JXRkhPTnFDK1c5RW1ZMTlzU1NVY3gyK3BZSXIwOEw1NVFUYVZnYWdJSzdma1h6Wm9ZcEZQN29SdTVXNE1WcnRrSGJ2ZURtdExBdFREc0U2dWhBMzRtcXpMZkVqdXlDS2hNbDU5aEpZZ0hYK0tNenlZaTV3NnNFdGdpRE81YVJhNldoYTRMNDBVaWZ4cngyYmd3eUJRaVprVXpmRUs5RzcyYkZvME1iUWdFbHFsak9WQ1NwMWVJTzVuY1ZyaTlmbXpmeXFHUk0ya084MVpBb2xuZkRwZEd1dkk4cnBXM2k5bkpNN09RWm5ENkJnOGN6bWhwS0QwSTgwV2ZKNiswK3ljZzJmU0ZXT0hmcytySjMrY3pOTFJHaFVkYmU3VTZDZHMvdFJRM3QzSml5QTRQQ2FaWENSSU56aEluQ1pwV3VmZ2s4UDFjVUU3bHhhVUtBaWNDVkdYbmlVb3B1aWIvU0pQTlNFeXpzMkQ4UVVxQmRWQ1lRMmg4Zk1NM3JtVzV2NVovR09nN3lYaW5kZ2VRcDA1M29iRUlYMmd4OGhJWE9sVUhHZXlyMzY1N3ZBQVpDbmdsYjRIa01QQlFXZXM1d3JuWGh0N3hpUE85Nkw4UU9XZlVUTTJ5eWQrSFkyUkJoOWx3S004ZlNuUE9xaXZUWVFablNvYUdkSEZtTlo0eFd6OTRoZVplK3I5TEtXUkVuelVSczVKc0ZubStIY1l5ZVRQWHVYNUtEZWdmam1RdGdJZ0cxNXdoUStPdHFFSmxQZFVHSklET0hwVTMwK1hwVGZ0VEFmZVlZd3o4V1VWaVZrUksyY0JPZEZabWRHbmgrREluQ3A2SFBPaUFUSjRjSHFhMzVDZ25VVXZUbzE4ZWZWeUVPZkJYMWw5M2RHdG9sZXNUTURwemJFVThzOW1yRDRRUk5RaFBGWnpEcVZkMmg3cU41SDVkQmlneWcyZlcvTVJvS0NJYjVpZXhxTjFOa3ZsSnpCb3RUekFwK0dhaWtsWnhRY1lISko3M2Jkd2lHQ1dZWTdVVkorMXBuQysyUGpmYWJXUWZaYi94MXVFaFJONFhKUzhmeGpyaU93a0dxZGNmZlpnTlpmSHF0T25vSkFPQ2poQ091cG5ycFkzNVhBTnA0ckJmdGNsWTZqQ2VYSmdxWm1PWXpZcWljMHRTb0xCd0VNWDBBa2VzSDJRN2NlTUhjTWhlVDNHay9JN3JUdzY0dFhVZWNtMjF4NWxrdGkzclFPTFZuMlRML1lCRWZwYXVGbWJMdUErcVAyTFlVUEZ4K0pYdC9JOFhvMUpsN0FIK2JsQlZ3aGR4dWpMdHU3UGxqb3NiaUd2a2RyMXF2emhmRE5PU2tlZ01iQnJMaEVUeDg5Q3hWNmRlYURqczZkY05SNlNKdThLZGRHYjJLUCt5aElIVmF3T0dKcUx3UDBtdHJHYms5U3JRQVdlOEo2SWMwZ1FDTUkyczlaWWFkbmlOR3BnMXNsSjBGVncwM1hUWG5sR0VmSGFjTm44WHNzVkQ0Tm90VmIraldCZVJqbzhQTXpvNkwxM2Y4UnZuTlVRUkFtdTdzcVExSnNtbzFRS2tUbGlOd2JIQW1xWHAvN1dyYm41U3B6QlphbndJM1A5UDNXUnpaSEVqaVhwMzFRejlWcG9GeWR2T1Bhb3lnS2lBdGxLMzZ2bnZUTUI3emJqSDlEaVRYZFRBWU02N3BvWXljSE9ESjFJSW5IbFRaY2FRUXAvYUl5T0pHQjI4WTR1UDV0bExRYVRFTTNLak9YNGlCUzRvOHg0LzRHbU5jZ2RPa2duTXk4VjRacDNLeTZDYktqTHVjdkZadkZQckRKN0RHVEhQWndrd09yV0NWOEdzWVZjNFhEbGNHMEJkeURXVTVjZllVOVNNQ2J5amh4WjBDTnBKNVJNN2w0ZDFiOFVHQm9hM2grZmhteUo2bHF3bkthRW56dEh1Ull0QUVsNitmdGxRNC9WZkc4Y3QzSmVGSXVyMmNqVUZqSG03dFVoTnkzNkwzMCtpL2dzMWNHbjE3amJWMDY3dVkxd0lEeDF4a0RBL05rMlJjcTRqdEdqcE5kdTNGRE1rSEJkOWcyRnFxaHowRzRVOXYzZkdzR1VaQU5wQWVLeXZ1UUlYRDhnaXF5QXFhZ1NwUmI4azFta3pHNkZKaGdRRVNZMzNkcmE3ajFTMmwvUGV5aDVGcURaa3BPbm91eFI5TDBxZ0dBQ0thMldKL1RjNzQxcTZrTzc3N1E2US9LM3F4OXpEUDZnU1dodVZEUmpPVis1ZnJpaTZhYko5VmlFNVNCMUtrVFFLRGVJN0IxYVE5ektlLyt6OGZsa05WQ3hyRkhXblFDSkI5M2hFenNrNGhoMUsrK3R6VmlWbmZ4VjZWTXQydFVDckJJMlpkMmVUZmJmZ0VGS1d2NXZybzB6bHNZbWpVMmRVUmxmN3NKVmxnakpna0ZtNkhQSi9KMmxmcWlDNW5kMEZvQnBqOGU4bCtGMlc1U3VUdXJmelhjbFErVk1RNU5tTWRDOTFiQ3lhdXIzczBuckkrVCtOcjlOeDNzS1JzZ3hoNkFsTFNpRUJ2djg1MEVuWlU0NWZkUy9pOG1kd090WHJlcXNkaHEyMTgxb0ZHK1JXZlpWY2xYRU5vWlZzdEhXOXcyQkdERXBaRWhaV0N5NHpuMGdrbS9Od25hWFRyU2dxR2RvUDNsUmd2RzQ5UTErYWlWckFvaUlTZjlTY01xRTBJNTZRSUlReDN6UU1JbmtuVUI5Z216VGhlblN2QklyU0w4SzQ1Rm11UzFQbUJ3WjJ2YVpZSHdMcU82d2ZSWlR1SlJYak9CWWNSSjdoQ1NRZDhsOTV1dmQ5YXNkSE81NjVaY0k3VjJ1NFJzWjNjWmNFY25EeGFZakFHKy9jUHdFRHVoc0FOaFFQZXhia25XeEEvaVF5MzJ6R0RLc2hJS2EvV01HWjV1eWV5c2NLemtIeHZXY215dkNaOXBOTlV2aEdxRWc4bTZHQU11VDFONkdFcmQ1TWZJSWVneGIwOHJSNnBCd2dsNUZhSU9HaGdEUkMvYzJQeW8yZlJCN3RmY2RCWDB1VGdmeENrQmR1ZllKUHNEazI5dVRWOEY4OVRNUHJGUkVManBUazhwbXdZNWx1TnRwc0tqRkJYTFpNbEk2eEk4N1JXZE5QdGxBcjV2Z2t6RnFKWERpd3Q1VHVybExaNmdhTWhRV0Q0THZCNnovekpUY3ZJUlYxTnNMSHNRRHNuRGp1eEpFZkFmSHdJK2NQbDVZcDlCalhVc0lUVFVYQ2o1NkdUSGdtd3EzOTdndk8xM3doaFJiRjZyU2tjbVd2blU5NmppekxZKzBRSUZtTCt1N1gvVk1kZ21XY3FuWURRUXF1ekhOcWkxeUtwMGIwYm9wOTFycmZmdVh0ZVlmSVJVbGh6Umltalpmd04vazBNd1AvOC9SZjRUQUFELy8xQkxCd2dPcnFqcm9Ra0FBS3NNQUFCUVN3TUVGQUFJQUFnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUM4QUFBQnBaRjl5YzJGZk9XWTFOekF6WmpVdFpEVXhPQzAwTmpKakxXSmtORFl0T0RCalpEaGtOV05rTXpReExuQjFZaFRIeVJKelNBQUE0RmY1NzJwS3hINzRxNlkxcllPRXRzdk5rcEJZWStmcHArYjRUVlAxenppbGZ3QUFRR0VmWndxWkk3OXEvMU1GQkNnQUFBaUlxcHE2Sk1tR0JydHhpUk5UMjZUWVAwOE9VcGVSVk1IUC9tQS9sNHdWelVsWkM0ZDlpOTEwRjk5c0d4eDd5RkhJMzVxdlZ4N3BvWDNSdkMwdGVvU2c1TVFLalFmM01CRnNmSmNhbm5tdGx0WkRaaVU4MVdMejBQYVlXOThiMmNMK0ZwMldydysyS1lQbVlxMlU3SnNCSE9ySUtKeExMejI3UnpGMjQveGw5MnFwbDBac2owU3ozOE9RZnR0U0xmTUt3c0EzWTdxWDZucDBXcG1wRnZhNHJ0THZiTmV3aVB1YTRkaHp4WkxwS01tQW44eSswRnMrTnBkcHpqQ0ZtWmROVWVIMVhYZ3hmWldFakNoZXdEUERGYjM4OUZNRWJHUWZiU1dvZGg3TzFxSTVtRHdSUFNvN3A5emx1LzhjV1RFV2Y0RWZ1V3BwdWQvTG5UTkM2SGdUaXFMTFFZVGE0a3MxVjlXaGZPc09kRGxkU3JWSzVTM2ZSaHVQUzJFTzlwVCtMa01UeXAxOTBSalB1YjB5c2Zzd0diemhPUUNHSmRZWkJYWE5OV2tXcytpZzlNQVd4TUF5NFBkMldyWGltWExCWjhTdTZ1aExaYkJPbEtjblFPcXpqckExVVRNNm5KZ2tQNlF2Z2szOVV1Q3pMcmpHYldRMUpibmE4Ujc3UGtFNlBSa2J0VTJ0UUdTMVQ2MHp1NnZFZEhrcnU0Y2h3aDh0SFl6c3h6K1JnZ2h0VHR0QzFvT2xnOEVoOTdQUzR5ckJoYmE4d3Z2azFUUW5SR3lBTjVFWVJUWmROUnFvbzd6cHptZnZYZTI5ZVhLdStadjBDZGtPL3ZnbWg2RmNobzhNQjlRdDBaL3BKTWM3UnA5VzR1YzU3SEg4Y3ZaTGRIYlFXUGJta2Noc3czVE05dmZ2bitJMU5QM3hiN3JNZmRrc3IvOENBQUQvLzFCTEJ3aUtRMnVxVlFJQUFPUUNBQUJRU3dFQ0ZBQVVBQWdBQ0FBQUFBQUFEcTZvNjZFSkFBQ3JEQUFBTHdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBYVdSZmNuTmhYemxtTlRjd00yWTFMV1ExTVRndE5EWXlZeTFpWkRRMkxUZ3dZMlE0WkRWalpETTBNUzV3WlcxUVN3RUNGQUFVQUFnQUNBQUFBQUFBaWtOcnFsVUNBQURrQWdBQUx3QUFBQUFBQUFBQUFBQUFBQUQrQ1FBQWFXUmZjbk5oWHpsbU5UY3dNMlkxTFdRMU1UZ3RORFl5WXkxaVpEUTJMVGd3WTJRNFpEVmpaRE0wTVM1d2RXSlFTd1VHQUFBQUFBSUFBZ0M2QUFBQXNBd0FBQUFBIiwiZmlsZW5hbWVzIjpbImlkX3JzYV85ZjU3MDNmNS1kNTE4LTQ2MmMtYmQ0Ni04MGNkOGQ1Y2QzNDEuemlwIiwiaWRfcnNhXzlmNTcwM2Y1LWQ1MTgtNDYyYy1iZDQ2LTgwY2Q4ZDVjZDM0MS5wZW0iLCJpZF9yc2FfOWY1NzAzZjUtZDUxOC00NjJjLWJkNDYtODBjZDhkNWNkMzQxLnB1YiJdfQo=","response_headers":{"Content-Security-Policy":"default-src 'self'; base-uri 'self'; form-action 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:5173; style-src 'self' 'unsafe-inline' http://localhost:5173 https://fonts.googleapis.com; img-src 'self' data: blob:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' http://localhost:5173 ws://localhost:5173 ws://localhost:8080; frame-ancestors 'none'","Content-Type":"application/json","Date":"Sun, 02 Nov 2025 17:16:13 GMT","Permissions-Policy":"geolocation=(), camera=(), microphone=(), interest-cohort=()","Referrer-Policy":"strict-origin-when-cross-origin","Vary":"Origin","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-Ratelimit-Limit":"100","X-Ratelimit-Remaining":"70","X-Ratelimit-Reset":"1762103820"},"retry":null,"status_code":200,"url":"http://localhost:8080/api/v1/ssh/9f5703f5-d518-462c-bd46-80cd8d5cd341/download?part=both\u0026mode=json"},"sensitive_attributes":[[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-KEY","type":"string"}}],[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-SECRET","type":"string"}}]]}]},{"module":"module.ssh[\"bastionKey\"]","mode":"managed","type":"autoglue_ssh_key","name":"this","provider":"provider[\"glueops/autoglue/autoglue\"]","instances":[{"schema_version":0,"attributes":{"bits":4096,"comment":"deploy@autoglue","created_at":"2025-11-02T17:16:13Z","fingerprint":"SHA256:ksju4vKj/QAEo8pfmkmPUXUbXrMIG/KqG/TlV9gQtZc","id":"9f5703f5-d518-462c-bd46-80cd8d5cd341","name":"Bastion Key","private_key_pem":null,"public_key":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDKG889JECnruXYKEw8XTzz4C+0rQhUqOiHTc8JvFtYgk6yOIXRax7f3mUyxV4+FTwljSgyayEjFtwumFNVAg47hFry4NKFClTR+pZckDgLN938Hsk7lNExX4vfwQwVoIWzLTGpOK9Al0Lv+9TKUCpkWJdP0o8ZnNdrnrtj3xhukul7myYEOfppajmgDgchCCUTKX/o8kkrPm91hu3y2v8qzmvVdXok143zvH8KPBYpHZ1xu/wcrl0stbH+H1eO++V2fdSX/286bQBSU51p2FeTaidU3WOymh6DOcVtLuEPHQZF/rBx4BM9MTZr37X7qUTWRDgLRj0M4JVCPSsFWW0yQ6kL5gDcDDpgfGPCR4G8aEhD5LTOFw5Hg6tUxa/juplV9nO0E1SPIeb7ni1bCIHtUAJL7kb+CGERK/3H3Fy+GUO67ULJCjIzLkBSK9d5bQOhkWj+bCkYBZS6C+ivrCmKFlrP47YYqFGu6O+qaAT3RA2XmWLlgQ2OXxXTTQFG/sJw+wsm6Q9DoaLzbMDQKR5LbMVVFHiEapJbq5ZFBFQ/KswuQvy3/UpPQMzhGXhYHdEueVMsSk/46W3UHw7QJdbs2E/ADr9wGPixoREfwS9cETw8iV3nCq5lcCV9gVNbHU+IYGZas9XxHFim85ttVoHXePx0WznCJuxlNY93l1n1w== deploy@autoglue","type":"rsa","updated_at":"2025-11-02T17:16:13Z"},"sensitive_attributes":[[{"type":"get_attr","value":"private_key_pem"}]]}]},{"module":"module.ssh[\"bastionKey\"]","mode":"managed","type":"local_sensitive_file","name":"zip","provider":"provider[\"registry.opentofu.org/hashicorp/local\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"content":null,"content_base64":"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","content_base64sha256":"ZnkpmE4AT50CSEgwvW9QojMy590Twn+KV0C5/CE0cNE=","content_base64sha512":"VZ7TYEXFf3jZ4m31773uEDiPG1BGBgVo9wn+gpV6k+nqoopD5FdcePh1tlMhHdnj/L7yWsmxXqQE2s8P2kmMWQ==","content_md5":"f61d1ed54685b30143f36950233df469","content_sha1":"160f3d8283320eebfad883632e49b79ba9467ec1","content_sha256":"667929984e004f9d02484830bd6f50a23332e7dd13c27f8a5740b9fc213470d1","content_sha512":"559ed36045c57f78d9e26df5efbdee10388f1b5046060568f709fe82957a93e9eaa28a43e4575c78f875b653211dd9e3fcbef25ac9b15ea404dacf0fda498c59","directory_permission":"0700","file_permission":"0700","filename":"out/bastionKey/id_rsa_9f5703f5-d518-462c-bd46-80cd8d5cd341.zip","id":"160f3d8283320eebfad883632e49b79ba9467ec1","source":null},"sensitive_attributes":[[{"type":"get_attr","value":"content_base64"}],[{"type":"get_attr","value":"content"}]],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.null_resource.mkdirs"]}]},{"module":"module.ssh[\"bastionKey\"]","mode":"managed","type":"null_resource","name":"mkdirs","provider":"provider[\"registry.opentofu.org/hashicorp/null\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"id":"3523105084924039922","triggers":null},"sensitive_attributes":[]}]},{"module":"module.ssh[\"clusterKey\"]","mode":"data","type":"http","name":"download","provider":"provider[\"registry.opentofu.org/hashicorp/http\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"body":"{\"id\":\"7898e3cc-3032-4c73-bd0d-9834916276a4\",\"name\":\"Cluster Key\",\"fingerprint\":\"SHA256:k4/yotTcaJMlJTIRtlkDfDAB/t6pZNdutwl37Lwmp5Y\",\"zip_base64\":\"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV83ODk4ZTNjYy0zMDMyLTRjNzMtYmQwZC05ODM0OTE2Mjc2YTQucGVt0gUBJ1d3Tz+FgCDPMMcQVwVv10iwKJevs4mzY6BjuVNgpIu3UVi5k7Onq6ePWamffo6PT0RZubNfgaGXU6hBWVCOo3luqlexW4Rfsaevr2lOsm92uVsuF9gYVz8XTKMBAQAA//9QSwcIZUFU4WUAAAB3AAAAUEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV83ODk4ZTNjYy0zMDMyLTRjNzMtYmQwZC05ODM0OTE2Mjc2YTQucHViKi7O0E1NMTI1NbRUcHR0dHQ29qtKdDbMiXLxNPQLcTUFiXn6ZJY4OUW4elvkJfkEu1oWFDkbFDiW+yQFhSSbBlaV+Pj6m/kVZ2aVh0Q5KyQlFpdk5uc5JJaW5KfnlKYCAgAA//9QSwcIJElwO2UAAABhAAAAUEsBAhQAFAAIAAgAAAAAAGVBVOFlAAAAdwAAADMAAAAAAAAAAAAAAAAAAAAAAGlkX2VkMjU1MTlfNzg5OGUzY2MtMzAzMi00YzczLWJkMGQtOTgzNDkxNjI3NmE0LnBlbVBLAQIUABQACAAIAAAAAAAkSXA7ZQAAAGEAAAAzAAAAAAAAAAAAAAAAAMYAAABpZF9lZDI1NTE5Xzc4OThlM2NjLTMwMzItNGM3My1iZDBkLTk4MzQ5MTYyNzZhNC5wdWJQSwUGAAAAAAIAAgDCAAAAjAEAAAAA\",\"filenames\":[\"id_ed25519_7898e3cc-3032-4c73-bd0d-9834916276a4.zip\",\"id_ed25519_7898e3cc-3032-4c73-bd0d-9834916276a4.pem\",\"id_ed25519_7898e3cc-3032-4c73-bd0d-9834916276a4.pub\"]}\n","ca_cert_pem":null,"client_cert_pem":null,"client_key_pem":null,"id":"http://localhost:8080/api/v1/ssh/7898e3cc-3032-4c73-bd0d-9834916276a4/download?part=both\u0026mode=json","insecure":null,"method":null,"request_body":null,"request_headers":{"Accept":"application/json","X-ORG-KEY":"org_lnJwmyyWH7JC-JgZo5v3Kw","X-ORG-SECRET":"fqd9yebGMfK6h5HSgWn4sXrwr9xlFbvbIYtNylRElMQ"},"request_timeout_ms":null,"response_body":"{\"id\":\"7898e3cc-3032-4c73-bd0d-9834916276a4\",\"name\":\"Cluster Key\",\"fingerprint\":\"SHA256:k4/yotTcaJMlJTIRtlkDfDAB/t6pZNdutwl37Lwmp5Y\",\"zip_base64\":\"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV83ODk4ZTNjYy0zMDMyLTRjNzMtYmQwZC05ODM0OTE2Mjc2YTQucGVt0gUBJ1d3Tz+FgCDPMMcQVwVv10iwKJevs4mzY6BjuVNgpIu3UVi5k7Onq6ePWamffo6PT0RZubNfgaGXU6hBWVCOo3luqlexW4Rfsaevr2lOsm92uVsuF9gYVz8XTKMBAQAA//9QSwcIZUFU4WUAAAB3AAAAUEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV83ODk4ZTNjYy0zMDMyLTRjNzMtYmQwZC05ODM0OTE2Mjc2YTQucHViKi7O0E1NMTI1NbRUcHR0dHQ29qtKdDbMiXLxNPQLcTUFiXn6ZJY4OUW4elvkJfkEu1oWFDkbFDiW+yQFhSSbBlaV+Pj6m/kVZ2aVh0Q5KyQlFpdk5uc5JJaW5KfnlKYCAgAA//9QSwcIJElwO2UAAABhAAAAUEsBAhQAFAAIAAgAAAAAAGVBVOFlAAAAdwAAADMAAAAAAAAAAAAAAAAAAAAAAGlkX2VkMjU1MTlfNzg5OGUzY2MtMzAzMi00YzczLWJkMGQtOTgzNDkxNjI3NmE0LnBlbVBLAQIUABQACAAIAAAAAAAkSXA7ZQAAAGEAAAAzAAAAAAAAAAAAAAAAAMYAAABpZF9lZDI1NTE5Xzc4OThlM2NjLTMwMzItNGM3My1iZDBkLTk4MzQ5MTYyNzZhNC5wdWJQSwUGAAAAAAIAAgDCAAAAjAEAAAAA\",\"filenames\":[\"id_ed25519_7898e3cc-3032-4c73-bd0d-9834916276a4.zip\",\"id_ed25519_7898e3cc-3032-4c73-bd0d-9834916276a4.pem\",\"id_ed25519_7898e3cc-3032-4c73-bd0d-9834916276a4.pub\"]}\n","response_body_base64":"eyJpZCI6Ijc4OThlM2NjLTMwMzItNGM3My1iZDBkLTk4MzQ5MTYyNzZhNCIsIm5hbWUiOiJDbHVzdGVyIEtleSIsImZpbmdlcnByaW50IjoiU0hBMjU2Oms0L3lvdFRjYUpNbEpUSVJ0bGtEZkRBQi90NnBaTmR1dHdsMzdMd21wNVkiLCJ6aXBfYmFzZTY0IjoiVUVzREJCUUFDQUFJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF6QUFBQWFXUmZaV1F5TlRVeE9WODNPRGs0WlROall5MHpNRE15TFRSak56TXRZbVF3WkMwNU9ETTBPVEUyTWpjMllUUXVjR1Z0MGdVQkoxZDNUeitGZ0NEUE1NY1FWd1Z2MTBpd0tKZXZzNG16WTZCanVWTmdwSXUzVVZpNWs3T25xNmVQV2FtZmZvNlBUMFJadWJOZmdhR1hVNmhCV1ZDT28zbHVxbGV4VzRSZnNhZXZyMmxPc205MnVWc3VGOWdZVno4WFRLTUJBUUFBLy85UVN3Y0laVUZVNFdVQUFBQjNBQUFBVUVzREJCUUFDQUFJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF6QUFBQWFXUmZaV1F5TlRVeE9WODNPRGs0WlROall5MHpNRE15TFRSak56TXRZbVF3WkMwNU9ETTBPVEUyTWpjMllUUXVjSFZpS2k3TzBFMU5NVEkxTmJSVWNIUjBkSFEyOXF0S2REYk1pWEx4TlBRTGNUVUZpWG42WkpZNE9VVzRlbHZrSmZrRXUxb1dGRGtiRkRpVyt5UUZoU1NiQmxhVitQajZtL2tWWjJhVmgwUTVLeVFsRnBkazV1YzVKSmFXNUtmbmxLWUNBZ0FBLy85UVN3Y0lKRWx3TzJVQUFBQmhBQUFBVUVzQkFoUUFGQUFJQUFnQUFBQUFBR1ZCVk9GbEFBQUFkd0FBQURNQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUdsa1gyVmtNalUxTVRsZk56ZzVPR1V6WTJNdE16QXpNaTAwWXpjekxXSmtNR1F0T1Rnek5Ea3hOakkzTm1FMExuQmxiVkJMQVFJVUFCUUFDQUFJQUFBQUFBQWtTWEE3WlFBQUFHRUFBQUF6QUFBQUFBQUFBQUFBQUFBQUFNWUFBQUJwWkY5bFpESTFOVEU1WHpjNE9UaGxNMk5qTFRNd016SXROR00zTXkxaVpEQmtMVGs0TXpRNU1UWXlOelpoTkM1d2RXSlFTd1VHQUFBQUFBSUFBZ0RDQUFBQWpBRUFBQUFBIiwiZmlsZW5hbWVzIjpbImlkX2VkMjU1MTlfNzg5OGUzY2MtMzAzMi00YzczLWJkMGQtOTgzNDkxNjI3NmE0LnppcCIsImlkX2VkMjU1MTlfNzg5OGUzY2MtMzAzMi00YzczLWJkMGQtOTgzNDkxNjI3NmE0LnBlbSIsImlkX2VkMjU1MTlfNzg5OGUzY2MtMzAzMi00YzczLWJkMGQtOTgzNDkxNjI3NmE0LnB1YiJdfQo=","response_headers":{"Content-Length":"1142","Content-Security-Policy":"default-src 'self'; base-uri 'self'; form-action 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:5173; style-src 'self' 'unsafe-inline' http://localhost:5173 https://fonts.googleapis.com; img-src 'self' data: blob:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' http://localhost:5173 ws://localhost:5173 ws://localhost:8080; frame-ancestors 'none'","Content-Type":"application/json","Date":"Sun, 02 Nov 2025 17:16:13 GMT","Permissions-Policy":"geolocation=(), camera=(), microphone=(), interest-cohort=()","Referrer-Policy":"strict-origin-when-cross-origin","Vary":"Origin","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-Ratelimit-Limit":"100","X-Ratelimit-Remaining":"71","X-Ratelimit-Reset":"1762103820"},"retry":null,"status_code":200,"url":"http://localhost:8080/api/v1/ssh/7898e3cc-3032-4c73-bd0d-9834916276a4/download?part=both\u0026mode=json"},"sensitive_attributes":[[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-KEY","type":"string"}}],[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-SECRET","type":"string"}}]]}]},{"module":"module.ssh[\"clusterKey\"]","mode":"managed","type":"autoglue_ssh_key","name":"this","provider":"provider[\"glueops/autoglue/autoglue\"]","instances":[{"schema_version":0,"attributes":{"bits":null,"comment":"bastion@autoglue","created_at":"2025-11-02T17:16:13Z","fingerprint":"SHA256:k4/yotTcaJMlJTIRtlkDfDAB/t6pZNdutwl37Lwmp5Y","id":"7898e3cc-3032-4c73-bd0d-9834916276a4","name":"Cluster Key","private_key_pem":null,"public_key":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILitBBXEK8nbLSE9prC0pAwLbRTc5QztLMO6NsijwTZC bastion@autoglue","type":"ed25519","updated_at":"2025-11-02T17:16:13Z"},"sensitive_attributes":[[{"type":"get_attr","value":"private_key_pem"}]]}]},{"module":"module.ssh[\"clusterKey\"]","mode":"managed","type":"local_sensitive_file","name":"zip","provider":"provider[\"registry.opentofu.org/hashicorp/local\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"content":null,"content_base64":"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV83ODk4ZTNjYy0zMDMyLTRjNzMtYmQwZC05ODM0OTE2Mjc2YTQucGVt0gUBJ1d3Tz+FgCDPMMcQVwVv10iwKJevs4mzY6BjuVNgpIu3UVi5k7Onq6ePWamffo6PT0RZubNfgaGXU6hBWVCOo3luqlexW4Rfsaevr2lOsm92uVsuF9gYVz8XTKMBAQAA//9QSwcIZUFU4WUAAAB3AAAAUEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV83ODk4ZTNjYy0zMDMyLTRjNzMtYmQwZC05ODM0OTE2Mjc2YTQucHViKi7O0E1NMTI1NbRUcHR0dHQ29qtKdDbMiXLxNPQLcTUFiXn6ZJY4OUW4elvkJfkEu1oWFDkbFDiW+yQFhSSbBlaV+Pj6m/kVZ2aVh0Q5KyQlFpdk5uc5JJaW5KfnlKYCAgAA//9QSwcIJElwO2UAAABhAAAAUEsBAhQAFAAIAAgAAAAAAGVBVOFlAAAAdwAAADMAAAAAAAAAAAAAAAAAAAAAAGlkX2VkMjU1MTlfNzg5OGUzY2MtMzAzMi00YzczLWJkMGQtOTgzNDkxNjI3NmE0LnBlbVBLAQIUABQACAAIAAAAAAAkSXA7ZQAAAGEAAAAzAAAAAAAAAAAAAAAAAMYAAABpZF9lZDI1NTE5Xzc4OThlM2NjLTMwMzItNGM3My1iZDBkLTk4MzQ5MTYyNzZhNC5wdWJQSwUGAAAAAAIAAgDCAAAAjAEAAAAA","content_base64sha256":"cogUt1gGjTe4ZH4N2fytacjBT49b7k3jc4UbtRftvV8=","content_base64sha512":"znImrPcna9M6YNNZlPzo3P04RuHWbePLHBtraR7tHzNO1ObMcVNKtsiVvtIl4ew6/4URxsvqMtUg/bSdzRi7hA==","content_md5":"f1acba3cf7667b9a7a3c780d1727f807","content_sha1":"0a8fee5f181a73adaac0404de74184db67c42a4b","content_sha256":"728814b758068d37b8647e0dd9fcad69c8c14f8f5bee4de373851bb517edbd5f","content_sha512":"ce7226acf7276bd33a60d35994fce8dcfd3846e1d66de3cb1c1b6b691eed1f334ed4e6cc71534ab6c895bed225e1ec3aff8511c6cbea32d520fdb49dcd18bb84","directory_permission":"0700","file_permission":"0700","filename":"out/clusterKey/id_ed25519_7898e3cc-3032-4c73-bd0d-9834916276a4.zip","id":"0a8fee5f181a73adaac0404de74184db67c42a4b","source":null},"sensitive_attributes":[[{"type":"get_attr","value":"content"}],[{"type":"get_attr","value":"content_base64"}]],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.null_resource.mkdirs"]}]},{"module":"module.ssh[\"clusterKey\"]","mode":"managed","type":"null_resource","name":"mkdirs","provider":"provider[\"registry.opentofu.org/hashicorp/null\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"id":"4173767452342259878","triggers":null},"sensitive_attributes":[]}]}],"check_results":[{"object_kind":"resource","config_addr":"module.servers.autoglue_server.this","status":"pass","objects":[{"object_addr":"module.servers.autoglue_server.this[\"manager1\"]","status":"pass"},{"object_addr":"module.servers.autoglue_server.this[\"agent1\"]","status":"pass"},{"object_addr":"module.servers.autoglue_server.this[\"bastion\"]","status":"pass"}]},{"object_kind":"resource","config_addr":"module.ssh.local_sensitive_file.zip","status":"pass","objects":[{"object_addr":"module.ssh[\"clusterKey\"].local_sensitive_file.zip[0]","status":"pass"},{"object_addr":"module.ssh[\"bastionKey\"].local_sensitive_file.zip[0]","status":"pass"}]}]} diff --git a/terraform/envs/dev/terraform.tfstate.backup b/terraform/envs/dev/terraform.tfstate.backup index c221858..e3add39 100644 --- a/terraform/envs/dev/terraform.tfstate.backup +++ b/terraform/envs/dev/terraform.tfstate.backup @@ -1 +1 @@ -{"version":4,"terraform_version":"1.10.6","serial":1,"lineage":"6ea5f588-9a03-6b57-9ea8-06e8e7fc2461","outputs":{"ssh_ids":{"value":{"key1":"8e9a0707-02ec-483d-abb7-1c45d199c116","key2":"44803472-4c2e-433f-a05d-38b1bbfb1e24"},"type":["object",{"key1":"string","key2":"string"}]},"ssh_public_keys":{"value":{"key1":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDN7NbCCLUqyhUFIpEznbNhvRg2mHMskfu/iV74kS7mvhc/v03CPheJad1rAiu50+5Ow8+ZKyrjmimw9Ph5Jp8vDlHNu8xio8AkDmvjiq0kGteGZETr368z2ZoDYBDpk1wIdvF8XxBZysKiPMjt/x/pg4xWPaJxZ+Nk2+lJt+l604AO7Bcs8vLChRn9GpaRoWz9KO2v0R9YNX8JUsifwJ6celXYXI1/IwCjpChNUyC/XdeU8hpgM7Au/f4h3L/slu8eTYSB+VsoIwITDIYiebsARj+949dnoM/SCoCOy6ljv35PnfKlD8f+nz8Jro5IkFc42HWk9P4CnDkLsXsqCpKo4VoufWI14VJaT39Pwe7gqdTnsr/vGHftJuUoCK7kEZFBQP4GrJjcvoWChJ72DEIKCcF3+Q4mp80sE9LsZAhmYyDbxueD7wQ+IA/edekU8kv6H2RFwNe8CQx0M3hSqEtACBNl72ogse691bxDmhyG7aaSOD0H9BtlZ4wR5c0OBq55BxRaKG1bok/8tFkctj+6uhbHdBUBHxu2nW5wkZdT75kEEZSCLEm7HHfiWVmZcc7uOAAVwinF+U7eT0ndjbYvz4yKV5+anz8mnMiWTV1y2PdJUGiGrCwQ9bQva4VeZT7HcxCMwqTwchvqjN+exHjjTUtKo9UkOXE0zwcA8nz+Gw== deploy1@autoglue","key2":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGSLRxWdx6Z4aNuoJ7GGmvjilsqbL7clLVdrXanYQryr deploy2@autoglue"},"type":["object",{"key1":"string","key2":"string"}]},"ssh_written_files":{"value":{"key1":["out/key1/id_rsa_8e9a0707-02ec-483d-abb7-1c45d199c116.zip"],"key2":["out/key2/id_rsa_44803472-4c2e-433f-a05d-38b1bbfb1e24.zip"]},"type":["object",{"key1":["list","string"],"key2":["list","string"]}]}},"resources":[{"module":"module.ssh[\"key1\"]","mode":"data","type":"http","name":"download","provider":"provider[\"registry.opentofu.org/hashicorp/http\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"body":"{\"id\":\"8e9a0707-02ec-483d-abb7-1c45d199c116\",\"name\":\"CI deploy key 1\",\"fingerprint\":\"SHA256:VxjPg+mm+WKYizRKUHSxB95D5lo961XcYZrnpQerTm0\",\"zip_base64\":\"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\",\"filenames\":[\"id_rsa_8e9a0707-02ec-483d-abb7-1c45d199c116.zip\",\"id_rsa_8e9a0707-02ec-483d-abb7-1c45d199c116.pem\",\"id_rsa_8e9a0707-02ec-483d-abb7-1c45d199c116.pub\"]}\n","ca_cert_pem":null,"client_cert_pem":null,"client_key_pem":null,"id":"http://localhost:8080/api/v1/ssh/8e9a0707-02ec-483d-abb7-1c45d199c116/download?part=both\u0026mode=json","insecure":null,"method":null,"request_body":null,"request_headers":{"Accept":"application/json","X-ORG-KEY":"org_lnJwmyyWH7JC-JgZo5v3Kw","X-ORG-SECRET":"fqd9yebGMfK6h5HSgWn4sXrwr9xlFbvbIYtNylRElMQ"},"request_timeout_ms":null,"response_body":"{\"id\":\"8e9a0707-02ec-483d-abb7-1c45d199c116\",\"name\":\"CI deploy key 1\",\"fingerprint\":\"SHA256:VxjPg+mm+WKYizRKUHSxB95D5lo961XcYZrnpQerTm0\",\"zip_base64\":\"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\",\"filenames\":[\"id_rsa_8e9a0707-02ec-483d-abb7-1c45d199c116.zip\",\"id_rsa_8e9a0707-02ec-483d-abb7-1c45d199c116.pem\",\"id_rsa_8e9a0707-02ec-483d-abb7-1c45d199c116.pub\"]}\n","response_body_base64":"eyJpZCI6IjhlOWEwNzA3LTAyZWMtNDgzZC1hYmI3LTFjNDVkMTk5YzExNiIsIm5hbWUiOiJDSSBkZXBsb3kga2V5IDEiLCJmaW5nZXJwcmludCI6IlNIQTI1NjpWeGpQZyttbStXS1lpelJLVUhTeEI5NUQ1bG85NjFYY1lacm5wUWVyVG0wIiwiemlwX2Jhc2U2NCI6IlVFc0RCQlFBQ0FBSUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBdkFBQUFhV1JmY25OaFh6aGxPV0V3TnpBM0xUQXlaV010TkRnelpDMWhZbUkzTFRGak5EVmtNVGs1WXpFeE5pNXdaVzFzbDdVU3JBb0NCWE8rNHViVUZtN0JCcmd6eUtBWk1MaTdmUDNXdTV1K2s1NnNxNVArenovalJGbTEvcmdlKzhkMjFZRDlpbjkwTWY3N0FLYXFhanFyY2l5cjgyd2xzbS94aGxmVklQbzJCWnludXlhQnZuRUd4OVpjdjRhbVVDZmVGNkFtSHNTS1J6YkZXTURWNGxFVGpyLzBVdGZqWngvZnpiWUhiMXR3WXBvMys0c1hYbG9hRitHKzJXbkdpODFxTExIaStMSnIzQnJWZ3lzQ0pBT2VKak9rTE1vNlpHTGx2SkxkcFVRN1liN3lVL3VxVkVtWFpQd3hneFp0OHJCc0UrdWN2YXljaS8zRENzY1ZBWVp0UE5ma0o1QzdoRjBxYlpEWDdpc2pscUVRUUh4Z3FDUE5GNk15QjZqMFdPVmpzaFBwVE4vQXJFNEVpM3BiVFlGWStGekdpWUhxNzRFb0xUdVZEaFdyc3M5MDN1VEY2NUhWY2FCWU1ZYk93bzRTVXJEaGFtSGI3WmhqaWdHL0JQWUFzd01wMEV4RGZFcCtQSTJMUGxhTnpQWVhaT2VMNENtSjBxdkZXejVTYW94SVl4V1NGM1lNOUtXVkE5ZEhuNkIwQ0tEdUdxTzhyTmZaNWlBNDJYVjhBZVNXSjhZTVVxb21yMkNxbStlcmtmdEIzVWRiU3N2Z2JJK1NIVFVaekFxbHN4R29RRnEwRzliR3htaVdiTTBBYTZaemN5RTYyYzc4Q1JidUw3WGhzTlUzS1poRk5aVGovSVcwUTB4NWZhQkQxMVlNd0VGT2xvU0ZLUmFSSlZUcFVUaTVIemIxSHBLYURSbHVvaGd4ZEt4VGVHTk93THAwdHMvSWtXdHk1SU5nd25HeUQxVE5yUjMzamVGOW53eVI4cUhhaXBWeWRiN0wvb04xakliRktHclFvTm4wUlRuTU5LVE5yOXAzeUMvZlc4d0x1QW1vbDJzVDdSQTIwRU9LQmgrOE1kTnBUYjJqRlRVanA3Y2NOeGZIWWNUd0ZqdlFoUDYyVEZwaGJZMkc2bzFuTDVFRldPY2ZoZG5rNmFIc0c1eXR6NTQzYVdnRG92WVBJWjR3UDltTjR5Vmh2V0dIU2ZNMEZ4a0M2TWt3MnhwT2Zpa3hZTGxJaHhyeHhiM3hSSnIwNFBlTkhIc2xVOHV0a1lMZ1EraGFWd2lZNm5aeFRrWlNUd2dwS2JUZmIxOXdSdHNFQmtBY0Z4YVlTTU5XbjN4aTk5d2tXaE8xV1RkYWRhZFVla1ZrUkw4NmlSRkdUVDlWWFNuN2xTYTNoRldLd3hsRmdqWFE0Y1lrOTJBdUJjNVoyUkxOci9rUlUxQit0V2JtbG5tYWw3Qjh3b2RrdmxQTWVKeVBJQ2QrRU1NamJ2a2g0N0lLakR5OEo4cDRPR2srNEx5VGRsdFpIb2ZpdmppODZTUjd5WHpuT2JFaW5mcktXS2gzTHRFVlJ4b1VHWXBoNDQ0Q0E1clNXR1dWSEEyUHZzKzdpNkI4YnNPSjFVd3RLaUFrT0FPaDJNV2lLMlRsbm1JeFUxMEhLME5ocXJleW1RVnZ3b0RkMGpYUC93U2FxVEdxVlBvRFQvQW9vZCs3WTEwaGNXY0RPWW43UzZWYWg3YWhwWDBvN0REZ3o4NjVRZXZBSmFZQUNTbTEyWTRIRHdOVEtkRlJpN3dhMDByeTFydnFZbGR0Q0tFam0vaTVPRThSNFAwNGl0TkowTU5PWmpJYTEyZVRBQkNSeGZLeGtQSDd2RS9VNXFjbGdxL2pYTU0rZ09hZ1FLay9RbVlBT3dYNnZBbWFaZ01zaHRpVktnWHowUmdQOVlHbG10RkM1RXUzdHhYM010V1ZZV0tYbDRSTmhXR1hFeFRxUjg5dVdpSHgwNDFnaHlRN0xrdkdUdUlRREtQNVRSQUF6SFBPa29hTVE0bzVIK05YWFNHajg0RmYxMlh1ZlYzalJIc1VDSGxrdTZPQzFicCtmODExUkJiRTE0ZEhIZ3F3dnE4bTNSaGpUK2R5SjFIVXB1QzVXcEhwZjNsRldxVE9KUHI3MGlDVitqQmlpOUJVWW5pZlFwbHhLUGNIbDZJaklMd1g3TkhYV2w2eFRFOFF1azljdFpmbjVPR3NnYjd2QllMbnEwd3ZoRUtoUmx1ai9Jckh6ZkF5VlBHcDZpbmhITUR4Q1B6V3kvNUtLOHozZEJwWnhVcitDcFo1ZjVPNmRBaUY2bkZLaS9ST2FGbVpEQnRvbmNNM2djSjZkc1Fpbkg4QXpYUTRTS25ybk5tWkdKNyt1T3lhTmJBU3YxK1U4SjFXSGR5K1J5Q2Z4MGNOUi9HRGR4eU5sNGU2UEkwYmZ4V0ZBV3BOMWRCWXpTN2pKL1VPQ1BKVjZ2Zmg4RENzcjZUeEVOQXFOREJwL0Z2T0hlcE93eS84MSs5NjYzSW05L2YxbHhNWXJSREhHT2ZFdFB2Q3IvOFRoaE1ETldzMVl4Z3dnZzJ0OXI2ZmV1RnEzeVBMSW1iN2Z2ajUrOFR0NEN4cEhKQk50bmxiZFJzMWNOY3BZUWlyTTJLWUxERStWQmloVHVXQi9TOVRqMjBZYVJuTUV0eENWcThaaURkTTFzVTNVdzVBcXZBNFlIUWpDZUtyMFpxUFZKVHVWTm5DcUoyeExVY0dIK2lrRUUrNmRJeFY2RjBBaDNieXVuelRRY3o2Q0xZTVRPdGJSU1ZOaDk2RklVR2RZWW5lNUtna1JROWNpTWVUdk5UNXFidjR6S0VIWFFkT0MzZmFVcVdTVEpKaEY4MXdCNkkwWVRRMGJsckJjdG95Wi8xbGZUbVI2MEhvcG12NTdtNUp6bkJKeGtMMkU1M09yOHBRdDRDbS92dkRXSlg3V2dOQXZhU3lWL1YxK2t1S1RjTnUxSXBzME5DSnBySm9wYXVTYi9XUXBVbVcvbFdZUlFjWFRqTlI4aXpRblVHWFdZSExGYzZJUDcySWY3dGZBYW9mVUpMTDkzZVBGRWlLaHBleERndE53K2FndENQQ2FtUFc2bDYyZ2Z3V3BOTjAzeWNBMkg3SkQzTURTU25jbHUvN0cxS2VJN3RyMWRCcjl5NGhnQ3dGNzY4czd0RmFFdlczNXRMMUZheFVTbm16WFBkV0JFbzhlS2o5U0xqQTlMd1VvUXBNazBBcWVWekordXBDSHc3Mzl6bStBZlZaU3l2QlJZemNxUHFsMEFMejVzVkE0UXdnUWxQUElNS3Qrd2wvQlc4RjZmMHVwNE1NeU53SW91YThpYTI1aTR2dXVRblVoYnpObnlselU0cFBEUGNhMXRnQzBtaU9tS2pVZkpqd0dOYks3NEpjYUgxN2FiczZSOUlPaVdwRW5sSHFtWjBsdURsSXZVMG5OcmVQUkxsQkVyM3lBRFRDQlhaU09kWVJ1SkpFTmp3dExJWElqcGIzV29ISnR1S2Q1a0tpVjlpVXpDaUw4Sm1mWHJoTVh6b1VDWGNhVmVDV0I5RUFjV2xyeFVVYWgxOVdXZDBFUWoxeG43T3orcjJzUTZRSW9wRGQ0Yzg0dW5JVmNhMG9odUl1RW1oZDdCUHd1Q3l2VU1oMWJsc1hZS3Y1Q2I3MStubnBNMFlGZjh5RTRDUENyOTBQWXR6WEJjeXp1RStJaU4wYmtPYkF2MlR0Z0d0K244NXFYSzJzWVo0SXkrbXp6TjJRSjcwSVNkLzJRcFdnQ3d2S2hWaUZabFZvaFVab1RuaDJMK21SdEI4V2RBSHN6U2xiRzdtakZUM210N3lFRm1iV0tWSDhMNjFNMXdqaTdYU053ZDEzaDhkckpJdlNidnZsRGVIcHp6a1VSbU1EUnRiU0hobE5hZmgxZnpMT3UrYTdmb1EraGZIdTFCWDIvZ3VZUTR0Q3NuK29Za25LU2paSm9SdlpoNDc5dWlFQkdIOHQvUjVxNFhneXlPSkhzNWVyZ1JLRVZGSlFHYkxGckxkVzdHM002ejNwckxTK0ZDNThuZ216Ykg5azdMZnhnV1dGVjBTMUJ1dUY2SEhBVlV6M1NVcGdvdnNLa0M5akdJcGVZSmFzNXBGbkxaOEZKRWR4a01rc2FFR293ZzVhcklGKzAySXAvaVdKUVhIMWhrcU1rT2xGV25hSG5kRWZoQnhvd21UTzRselA0clc1dU5McGVkYU1obHZwQmw0WjdwTUNQNzJ3MzB3RXkyOFh5TnlER2xTZkR5VGlGcXlPNFRRT2txL0RQaURjcHladkRGUkFOK1hRYjNMWW5KdWFmQllWQjBCb0hINEY4V1k2bVgwTHpJWGtudnhOdm1Tb05wdVd5cmRaWGI1OFNqUnMwWVI1RlB0amYycEpIc0pucGY4TC9FME8wUkwrUFVYK0Z3QUEvLzlRU3djSXZJOWIyYUlKQUFDckRBQUFVRXNEQkJRQUNBQUlBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQXZBQUFBYVdSZmNuTmhYemhsT1dFd056QTNMVEF5WldNdE5EZ3paQzFoWW1JM0xURmpORFZrTVRrNVl6RXhOaTV3ZFdJVXg4a1NjMGdBQU9CWCtlOXF5aEtpSGY2cWFVMXNpVWlDSkc2V1R0QzJhRFNlZm1xT0g2WGxQeU5OLzBBSW9YN3c5eFNKV3k2Wi85T0FONmhEQ0JHOEdiN3Fad2lkbzk5V1JpZG5NUGN1ODh2bC9wVmErMExKWithcldKWEpRMjJYTXVjWDRZQ0NFcnRwSVk2d21oV0JVNjRNY0ltM2pYVmJ0VXdMU3NVZHdHSTB0aitEdGVvQkpFYTcxTlZQSU5hRXJjUU14OE1SN0ZMU0cyL2RHSWpJbkdJNWdkZXFKeHYxcXVCU1QvektEMTk1ZlFhcHV5YWNUeVN1Y1NldU9Rb3l2S3A2VHNGeVJ1VzkwNndodmZmUFhmT3UwaUxjdGJmL0FtNUVxdzl6anpsdVh1K1hJL0lPUS9XQVNqL2FFUDhxY0FUSzRYdFI0Y3gvNVBKdzVta3pBeHkrSHpvWDA5NWhUbWc0N3dwbkZONXJUcE8xb3Vzdi9BUDE2TG9kbTNvNUtFSDM4Um9EZkxodUIrN1lLdzQ1NWJKa1A0a1d5S2d6eUptKzZBOE5YaS9IL2Z4NU9xSWN1Mmw0MEFLRzFlK3ZDRHM2OG90bGZ5WjNqbnJrcWNSTVR2b3RrSzNScmZPbGY2TFNWU1hEZER5VW53N2NUVzRISUZCVE85TUVsdTE3TTdKMXhvYkticHdEZVZ4Z0VnR3lIRzNwZm1JK0J1aTJDcGREK2ZpWkUwUzYzNmhTLzZYNHFJblphclRsWnFscCtyZ2FncTNwVTVQSTdLN2t3bFgvS1lxKzNsUFBFck9lOEdBNmtYeXF1ZU5jWm5haFI3cTl6bEwzVkJoSmlsQlZpR2ttRDNRMlc5VzJQOVV6YnBNOFYrY3JoREdydWhNWHFUZ1V1cUxPM3NzdWIxNnNjR20zZzdhN1ZNOHdGamNwS056SXFxd1JzWnVXM1paVWpuRVNxbmErb2d2N2hTd3ZsMS90YzNpMTZ6cU1KcS9YSW5KOW1jTE9jZ2k2bmJQWTM3OS9DancwL1NiK204NVQvMjFtL0Y4QUFBRC8vMUJMQndqZ2pPZEVWd0lBQU9VQ0FBQlFTd0VDRkFBVUFBZ0FDQUFBQUFBQXZJOWIyYUlKQUFDckRBQUFMd0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFhV1JmY25OaFh6aGxPV0V3TnpBM0xUQXlaV010TkRnelpDMWhZbUkzTFRGak5EVmtNVGs1WXpFeE5pNXdaVzFRU3dFQ0ZBQVVBQWdBQ0FBQUFBQUE0SXpuUkZjQ0FBRGxBZ0FBTHdBQUFBQUFBQUFBQUFBQUFBRC9DUUFBYVdSZmNuTmhYemhsT1dFd056QTNMVEF5WldNdE5EZ3paQzFoWW1JM0xURmpORFZrTVRrNVl6RXhOaTV3ZFdKUVN3VUdBQUFBQUFJQUFnQzZBQUFBc3d3QUFBQUEiLCJmaWxlbmFtZXMiOlsiaWRfcnNhXzhlOWEwNzA3LTAyZWMtNDgzZC1hYmI3LTFjNDVkMTk5YzExNi56aXAiLCJpZF9yc2FfOGU5YTA3MDctMDJlYy00ODNkLWFiYjctMWM0NWQxOTljMTE2LnBlbSIsImlkX3JzYV84ZTlhMDcwNy0wMmVjLTQ4M2QtYWJiNy0xYzQ1ZDE5OWMxMTYucHViIl19Cg==","response_headers":{"Content-Security-Policy":"default-src 'self'; base-uri 'self'; form-action 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:5173; style-src 'self' 'unsafe-inline' http://localhost:5173 https://fonts.googleapis.com; img-src 'self' data: blob:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' http://localhost:5173 ws://localhost:5173 ws://localhost:8080; frame-ancestors 'none'","Content-Type":"application/json","Date":"Sat, 01 Nov 2025 07:04:39 GMT","Permissions-Policy":"geolocation=(), camera=(), microphone=(), interest-cohort=()","Referrer-Policy":"strict-origin-when-cross-origin","Vary":"Origin","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-Ratelimit-Limit":"100","X-Ratelimit-Remaining":"96","X-Ratelimit-Reset":"1761980700"},"retry":null,"status_code":200,"url":"http://localhost:8080/api/v1/ssh/8e9a0707-02ec-483d-abb7-1c45d199c116/download?part=both\u0026mode=json"},"sensitive_attributes":[[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-KEY","type":"string"}}],[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-SECRET","type":"string"}}]]}]},{"module":"module.ssh[\"key1\"]","mode":"managed","type":"autoglue_ssh_key","name":"this","provider":"provider[\"glueops/autoglue/autoglue\"]","instances":[{"schema_version":0,"attributes":{"bits":4096,"comment":"deploy1@autoglue","created_at":"2025-11-01T07:04:39Z","fingerprint":"SHA256:VxjPg+mm+WKYizRKUHSxB95D5lo961XcYZrnpQerTm0","id":"8e9a0707-02ec-483d-abb7-1c45d199c116","name":"CI deploy key 1","private_key_pem":null,"public_key":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDN7NbCCLUqyhUFIpEznbNhvRg2mHMskfu/iV74kS7mvhc/v03CPheJad1rAiu50+5Ow8+ZKyrjmimw9Ph5Jp8vDlHNu8xio8AkDmvjiq0kGteGZETr368z2ZoDYBDpk1wIdvF8XxBZysKiPMjt/x/pg4xWPaJxZ+Nk2+lJt+l604AO7Bcs8vLChRn9GpaRoWz9KO2v0R9YNX8JUsifwJ6celXYXI1/IwCjpChNUyC/XdeU8hpgM7Au/f4h3L/slu8eTYSB+VsoIwITDIYiebsARj+949dnoM/SCoCOy6ljv35PnfKlD8f+nz8Jro5IkFc42HWk9P4CnDkLsXsqCpKo4VoufWI14VJaT39Pwe7gqdTnsr/vGHftJuUoCK7kEZFBQP4GrJjcvoWChJ72DEIKCcF3+Q4mp80sE9LsZAhmYyDbxueD7wQ+IA/edekU8kv6H2RFwNe8CQx0M3hSqEtACBNl72ogse691bxDmhyG7aaSOD0H9BtlZ4wR5c0OBq55BxRaKG1bok/8tFkctj+6uhbHdBUBHxu2nW5wkZdT75kEEZSCLEm7HHfiWVmZcc7uOAAVwinF+U7eT0ndjbYvz4yKV5+anz8mnMiWTV1y2PdJUGiGrCwQ9bQva4VeZT7HcxCMwqTwchvqjN+exHjjTUtKo9UkOXE0zwcA8nz+Gw== deploy1@autoglue","type":"rsa","updated_at":"2025-11-01T07:04:39Z"},"sensitive_attributes":[[{"type":"get_attr","value":"private_key_pem"}]]}]},{"module":"module.ssh[\"key1\"]","mode":"managed","type":"local_sensitive_file","name":"zip","provider":"provider[\"registry.opentofu.org/hashicorp/local\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"content":null,"content_base64":"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","content_base64sha256":"Q5kZRm04HuQYtQ/cU2H8oz9IOJzhJ1l7m31cvPyEd8Q=","content_base64sha512":"ubkgmay6pwDlkJfhTB/qdZb9ges5ZjN3qCe8NnCxcl7zCHPoindvqZY67ORIuteHWQPQkTnM1KhJJeg1ZgsS2Q==","content_md5":"e1e587a1eacf0fbc2b54c17d6c5264a4","content_sha1":"323cb9d982aefd49ea4d0a75e1acf9b44d53142c","content_sha256":"439919466d381ee418b50fdc5361fca33f48389ce127597b9b7d5cbcfc8477c4","content_sha512":"b9b92099acbaa700e59097e14c1fea7596fd81eb39663377a827bc3670b1725ef30873e88a776fa9963aece448bad7875903d09139ccd4a84925e835660b12d9","directory_permission":"0700","file_permission":"0700","filename":"out/key1/id_rsa_8e9a0707-02ec-483d-abb7-1c45d199c116.zip","id":"323cb9d982aefd49ea4d0a75e1acf9b44d53142c","source":null},"sensitive_attributes":[[{"type":"get_attr","value":"content"}],[{"type":"get_attr","value":"content_base64"}]],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.null_resource.mkdirs"]}]},{"module":"module.ssh[\"key1\"]","mode":"managed","type":"null_resource","name":"mkdirs","provider":"provider[\"registry.opentofu.org/hashicorp/null\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"id":"1448153292650130154","triggers":null},"sensitive_attributes":[]}]},{"module":"module.ssh[\"key2\"]","mode":"data","type":"http","name":"download","provider":"provider[\"registry.opentofu.org/hashicorp/http\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"body":"{\"id\":\"44803472-4c2e-433f-a05d-38b1bbfb1e24\",\"name\":\"CI deploy key 2\",\"fingerprint\":\"SHA256:bjV3SJznTf1PWIH8D31HyFFxy4Znk1+rz4X25a02iWg\",\"zip_base64\":\"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAvAAAAaWRfcnNhXzQ0ODAzNDcyLTRjMmUtNDMzZi1hMDVkLTM4YjFiYmZiMWUyNC5wZW3SBQEnV3dPP4WAIM8wxxBXBW/XSLAol6+zibNjoGO5U2Cki7dRWLmTs6erp7NpgX6ltmWxqYerc2Z2UJSrYUVYcnKOSVGGd2KJmaVJRap2oktmjlGOizYX2BhXPxdMowEBAAD//1BLBwiA5PrAZQAAAHcAAABQSwMEFAAIAAgAAAAAAAAAAAAAAAAAAAAAAC8AAABpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnB1YiouztBNTTEyNTW0VHB0dHR0NvarSnQ2zIly8TT0C3E1BYl5ugf7BFWEp1SYRZkk+pXme5m7u+eWZWXmFBcm+Zgn5/iEpRRFJOZFBhZVFimkpBbk5FcaOSSWluSn55SmAgIAAP//UEsHCMVvvVxlAAAAYQAAAFBLAQIUABQACAAIAAAAAACA5PrAZQAAAHcAAAAvAAAAAAAAAAAAAAAAAAAAAABpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnBlbVBLAQIUABQACAAIAAAAAADFb71cZQAAAGEAAAAvAAAAAAAAAAAAAAAAAMIAAABpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnB1YlBLBQYAAAAAAgACALoAAACEAQAAAAA=\",\"filenames\":[\"id_rsa_44803472-4c2e-433f-a05d-38b1bbfb1e24.zip\",\"id_rsa_44803472-4c2e-433f-a05d-38b1bbfb1e24.pem\",\"id_rsa_44803472-4c2e-433f-a05d-38b1bbfb1e24.pub\"]}\n","ca_cert_pem":null,"client_cert_pem":null,"client_key_pem":null,"id":"http://localhost:8080/api/v1/ssh/44803472-4c2e-433f-a05d-38b1bbfb1e24/download?part=both\u0026mode=json","insecure":null,"method":null,"request_body":null,"request_headers":{"Accept":"application/json","X-ORG-KEY":"org_lnJwmyyWH7JC-JgZo5v3Kw","X-ORG-SECRET":"fqd9yebGMfK6h5HSgWn4sXrwr9xlFbvbIYtNylRElMQ"},"request_timeout_ms":null,"response_body":"{\"id\":\"44803472-4c2e-433f-a05d-38b1bbfb1e24\",\"name\":\"CI deploy key 2\",\"fingerprint\":\"SHA256:bjV3SJznTf1PWIH8D31HyFFxy4Znk1+rz4X25a02iWg\",\"zip_base64\":\"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAvAAAAaWRfcnNhXzQ0ODAzNDcyLTRjMmUtNDMzZi1hMDVkLTM4YjFiYmZiMWUyNC5wZW3SBQEnV3dPP4WAIM8wxxBXBW/XSLAol6+zibNjoGO5U2Cki7dRWLmTs6erp7NpgX6ltmWxqYerc2Z2UJSrYUVYcnKOSVGGd2KJmaVJRap2oktmjlGOizYX2BhXPxdMowEBAAD//1BLBwiA5PrAZQAAAHcAAABQSwMEFAAIAAgAAAAAAAAAAAAAAAAAAAAAAC8AAABpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnB1YiouztBNTTEyNTW0VHB0dHR0NvarSnQ2zIly8TT0C3E1BYl5ugf7BFWEp1SYRZkk+pXme5m7u+eWZWXmFBcm+Zgn5/iEpRRFJOZFBhZVFimkpBbk5FcaOSSWluSn55SmAgIAAP//UEsHCMVvvVxlAAAAYQAAAFBLAQIUABQACAAIAAAAAACA5PrAZQAAAHcAAAAvAAAAAAAAAAAAAAAAAAAAAABpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnBlbVBLAQIUABQACAAIAAAAAADFb71cZQAAAGEAAAAvAAAAAAAAAAAAAAAAAMIAAABpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnB1YlBLBQYAAAAAAgACALoAAACEAQAAAAA=\",\"filenames\":[\"id_rsa_44803472-4c2e-433f-a05d-38b1bbfb1e24.zip\",\"id_rsa_44803472-4c2e-433f-a05d-38b1bbfb1e24.pem\",\"id_rsa_44803472-4c2e-433f-a05d-38b1bbfb1e24.pub\"]}\n","response_body_base64":"eyJpZCI6IjQ0ODAzNDcyLTRjMmUtNDMzZi1hMDVkLTM4YjFiYmZiMWUyNCIsIm5hbWUiOiJDSSBkZXBsb3kga2V5IDIiLCJmaW5nZXJwcmludCI6IlNIQTI1NjpialYzU0p6blRmMVBXSUg4RDMxSHlGRnh5NFpuazErcno0WDI1YTAyaVdnIiwiemlwX2Jhc2U2NCI6IlVFc0RCQlFBQ0FBSUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBdkFBQUFhV1JmY25OaFh6UTBPREF6TkRjeUxUUmpNbVV0TkRNelppMWhNRFZrTFRNNFlqRmlZbVppTVdVeU5DNXdaVzNTQlFFblYzZFBQNFdBSU04d3h4QlhCVy9YU0xBb2w2K3ppYk5qb0dPNVUyQ2tpN2RSV0xtVHM2ZXJwN05wZ1g2bHRtV3hxWWVyYzJaMlVKU3JZVVZZY25LT1NWR0dkMktKbWFWSlJhcDJva3RtamxHT2l6WVgyQmhYUHhkTW93RUJBQUQvLzFCTEJ3aUE1UHJBWlFBQUFIY0FBQUJRU3dNRUZBQUlBQWdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQzhBQUFCcFpGOXljMkZmTkRRNE1ETTBOekl0TkdNeVpTMDBNek5tTFdFd05XUXRNemhpTVdKaVptSXhaVEkwTG5CMVlpb3V6dEJOVFRFeU5UVzBWSEIwZEhSME52YXJTblEyeklseThUVDBDM0UxQllsNXVnZjdCRldFcDFTWVJaa2srcFhtZTVtN3UrZVdaV1htRkJjbStaZ241L2lFcFJSRkpPWkZCaFpWRmlta3BCYms1RmNhT1NTV2x1U241NVNtQWdJQUFQLy9VRXNIQ01WdnZWeGxBQUFBWVFBQUFGQkxBUUlVQUJRQUNBQUlBQUFBQUFDQTVQckFaUUFBQUhjQUFBQXZBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQnBaRjl5YzJGZk5EUTRNRE0wTnpJdE5HTXlaUzAwTXpObUxXRXdOV1F0TXpoaU1XSmlabUl4WlRJMExuQmxiVkJMQVFJVUFCUUFDQUFJQUFBQUFBREZiNzFjWlFBQUFHRUFBQUF2QUFBQUFBQUFBQUFBQUFBQUFNSUFBQUJwWkY5eWMyRmZORFE0TURNME56SXROR015WlMwME16Tm1MV0V3TldRdE16aGlNV0ppWm1JeFpUSTBMbkIxWWxCTEJRWUFBQUFBQWdBQ0FMb0FBQUNFQVFBQUFBQT0iLCJmaWxlbmFtZXMiOlsiaWRfcnNhXzQ0ODAzNDcyLTRjMmUtNDMzZi1hMDVkLTM4YjFiYmZiMWUyNC56aXAiLCJpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnBlbSIsImlkX3JzYV80NDgwMzQ3Mi00YzJlLTQzM2YtYTA1ZC0zOGIxYmJmYjFlMjQucHViIl19Cg==","response_headers":{"Content-Length":"1114","Content-Security-Policy":"default-src 'self'; base-uri 'self'; form-action 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:5173; style-src 'self' 'unsafe-inline' http://localhost:5173 https://fonts.googleapis.com; img-src 'self' data: blob:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' http://localhost:5173 ws://localhost:5173 ws://localhost:8080; frame-ancestors 'none'","Content-Type":"application/json","Date":"Sat, 01 Nov 2025 07:04:38 GMT","Permissions-Policy":"geolocation=(), camera=(), microphone=(), interest-cohort=()","Referrer-Policy":"strict-origin-when-cross-origin","Vary":"Origin","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-Ratelimit-Limit":"100","X-Ratelimit-Remaining":"97","X-Ratelimit-Reset":"1761980700"},"retry":null,"status_code":200,"url":"http://localhost:8080/api/v1/ssh/44803472-4c2e-433f-a05d-38b1bbfb1e24/download?part=both\u0026mode=json"},"sensitive_attributes":[[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-KEY","type":"string"}}],[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-SECRET","type":"string"}}]]}]},{"module":"module.ssh[\"key2\"]","mode":"managed","type":"autoglue_ssh_key","name":"this","provider":"provider[\"glueops/autoglue/autoglue\"]","instances":[{"schema_version":0,"attributes":{"bits":null,"comment":"deploy2@autoglue","created_at":"2025-11-01T07:04:38Z","fingerprint":"SHA256:bjV3SJznTf1PWIH8D31HyFFxy4Znk1+rz4X25a02iWg","id":"44803472-4c2e-433f-a05d-38b1bbfb1e24","name":"CI deploy key 2","private_key_pem":null,"public_key":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGSLRxWdx6Z4aNuoJ7GGmvjilsqbL7clLVdrXanYQryr deploy2@autoglue","type":"ed25519","updated_at":"2025-11-01T07:04:38Z"},"sensitive_attributes":[[{"type":"get_attr","value":"private_key_pem"}]]}]},{"module":"module.ssh[\"key2\"]","mode":"managed","type":"local_sensitive_file","name":"zip","provider":"provider[\"registry.opentofu.org/hashicorp/local\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"content":null,"content_base64":"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAvAAAAaWRfcnNhXzQ0ODAzNDcyLTRjMmUtNDMzZi1hMDVkLTM4YjFiYmZiMWUyNC5wZW3SBQEnV3dPP4WAIM8wxxBXBW/XSLAol6+zibNjoGO5U2Cki7dRWLmTs6erp7NpgX6ltmWxqYerc2Z2UJSrYUVYcnKOSVGGd2KJmaVJRap2oktmjlGOizYX2BhXPxdMowEBAAD//1BLBwiA5PrAZQAAAHcAAABQSwMEFAAIAAgAAAAAAAAAAAAAAAAAAAAAAC8AAABpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnB1YiouztBNTTEyNTW0VHB0dHR0NvarSnQ2zIly8TT0C3E1BYl5ugf7BFWEp1SYRZkk+pXme5m7u+eWZWXmFBcm+Zgn5/iEpRRFJOZFBhZVFimkpBbk5FcaOSSWluSn55SmAgIAAP//UEsHCMVvvVxlAAAAYQAAAFBLAQIUABQACAAIAAAAAACA5PrAZQAAAHcAAAAvAAAAAAAAAAAAAAAAAAAAAABpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnBlbVBLAQIUABQACAAIAAAAAADFb71cZQAAAGEAAAAvAAAAAAAAAAAAAAAAAMIAAABpZF9yc2FfNDQ4MDM0NzItNGMyZS00MzNmLWEwNWQtMzhiMWJiZmIxZTI0LnB1YlBLBQYAAAAAAgACALoAAACEAQAAAAA=","content_base64sha256":"ZRPiFqb0a5WdSSb+coKbsQKnLZJRnS+a54zTRj9JnuE=","content_base64sha512":"/Zih3xdtPz8/s7/1o3Q+i2YPSwRaRYKeJS1SDSdm/yQ45Tn91aJgsLC7RemQl2wDt9pcvcCa/qJFPZ1KT4vYSw==","content_md5":"602d52ba829b8fb9f09dad071d48ed0c","content_sha1":"2dc8e325dd54881040524fdc98a233a329ea5a77","content_sha256":"6513e216a6f46b959d4926fe72829bb102a72d92519d2f9ae78cd3463f499ee1","content_sha512":"fd98a1df176d3f3f3fb3bff5a3743e8b660f4b045a45829e252d520d2766ff2438e539fdd5a260b0b0bb45e990976c03b7da5cbdc09afea2453d9d4a4f8bd84b","directory_permission":"0700","file_permission":"0700","filename":"out/key2/id_rsa_44803472-4c2e-433f-a05d-38b1bbfb1e24.zip","id":"2dc8e325dd54881040524fdc98a233a329ea5a77","source":null},"sensitive_attributes":[[{"type":"get_attr","value":"content"}],[{"type":"get_attr","value":"content_base64"}]],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.null_resource.mkdirs"]}]},{"module":"module.ssh[\"key2\"]","mode":"managed","type":"null_resource","name":"mkdirs","provider":"provider[\"registry.opentofu.org/hashicorp/null\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"id":"6741240159027812521","triggers":null},"sensitive_attributes":[]}]}],"check_results":[{"object_kind":"resource","config_addr":"module.ssh.local_sensitive_file.zip","status":"pass","objects":[{"object_addr":"module.ssh[\"key2\"].local_sensitive_file.zip[0]","status":"pass"},{"object_addr":"module.ssh[\"key1\"].local_sensitive_file.zip[0]","status":"pass"}]}]} +{"version":4,"terraform_version":"1.10.6","serial":1,"lineage":"48e22363-3129-407c-1198-34f8c4d9e309","outputs":{"server_ids":{"value":{"agent1":"d39e26d8-1669-4f53-a01f-ad0353f52b81","bastion":"bd33a447-593f-4d62-a325-225acd891c84","manager1":"9b7d4f40-80b9-48fc-a09f-2c9a9a022be9"},"type":["object",{"agent1":"string","bastion":"string","manager1":"string"}]},"server_statuses":{"value":{"agent1":"pending","bastion":"pending","manager1":"pending"},"type":["object",{"agent1":"string","bastion":"string","manager1":"string"}]},"ssh_ids":{"value":{"bastionKey":"7a7d5424-f311-4c42-b882-0eb41471fbcc","clusterKey":"59699815-6d51-4e6f-b5b9-037781161bc4"},"type":["object",{"bastionKey":"string","clusterKey":"string"}]},"ssh_public_keys":{"value":{"bastionKey":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDG9eXyUYgcj9fTN1Z2uNf/PFmpr6r08cOG+WJg/pGXtEGhOYBWg63uetSDfap3UbjZsmOsxkjMmR+A19qoCfWyP4xa1OPyk0LPhUHsjKXSxjeo+RJixRPRPKVK3ipI/vHCVBYunov9Ad2oYOyzruPm8URCGxjwKkIx3WIPwaRwMKA9vESdXChpoeYv8oWWNopNaqIqPyR61FJsII7e0Jnod/887ZQuQiqyRkf/InkYpcmjZ3vs6AHogo+JpMrZuLvv4Dfp72N81nDuvG4AlOBeXkEGIMSrzqIbPR2069tT6gZwdCOzoxfHGbVYiyL7NAwINFzapI7/hw85/y8VevNOYWiz8LM09fgnT/ontQiN7Cd0EH8zxobC7vTtBJUNBaiheTwRlV98WBiYj/9XMy87dCwemp3T565K81CLal0pUHZdHj47Usvp7o2BAe+wkidAlueJbDS2kfhFzzynSIP5OIGewwIlKPsuuNAkWkua68yGXrEX+044JwJkFHA8jEe6JLb+t5G9s1iYJrqj8ii+yLwyDlYHYxDRO6Iop3HYbjBBimT6AKpVTQR5W0FjYlTh9wHTRXqtOnnh+SXmLSzayQC6uG0ubF/qta9rMRAnb9dgGre0WlszApLWS1B6NX1u4OnaG+HrajbiG16XXATHWiUp/3BNdFgfFQNFcwFJTw== deploy@autoglue","clusterKey":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6MGsDBAuSvabsZXltSTkzab358Lxi7qMbFrrYJh/bo bastion@autoglue"},"type":["object",{"bastionKey":"string","clusterKey":"string"}]},"ssh_written_files":{"value":{"bastionKey":["out/bastionKey/id_rsa_7a7d5424-f311-4c42-b882-0eb41471fbcc.zip"],"clusterKey":["out/clusterKey/id_ed25519_59699815-6d51-4e6f-b5b9-037781161bc4.zip"]},"type":["object",{"bastionKey":["list","string"],"clusterKey":["list","string"]}]}},"resources":[{"module":"module.servers","mode":"managed","type":"autoglue_server","name":"this","provider":"provider[\"glueops/autoglue/autoglue\"]","instances":[{"index_key":"agent1","schema_version":0,"attributes":{"created_at":"2025-11-02T17:14:32.664168Z","hostname":"k3s-agent-01","id":"d39e26d8-1669-4f53-a01f-ad0353f52b81","organization_id":"11a40e2e-fff9-49ff-84cf-60f6aff488ed","private_ip_address":"10.0.2.21","public_ip_address":null,"raw":"{\"created_at\":\"2025-11-02T17:14:32.664168Z\",\"hostname\":\"k3s-agent-01\",\"id\":\"d39e26d8-1669-4f53-a01f-ad0353f52b81\",\"organization_id\":\"11a40e2e-fff9-49ff-84cf-60f6aff488ed\",\"private_ip_address\":\"10.0.2.21\",\"role\":\"agent\",\"ssh_key_id\":\"59699815-6d51-4e6f-b5b9-037781161bc4\",\"ssh_user\":\"ubuntu\",\"status\":\"pending\",\"updated_at\":\"2025-11-02T17:14:32.664168Z\"}","role":"agent","ssh_key_id":"59699815-6d51-4e6f-b5b9-037781161bc4","ssh_user":"ubuntu","status":"pending","updated_at":"2025-11-02T17:14:32.664168Z"},"sensitive_attributes":[],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.local_file.public_key","module.ssh.local_sensitive_file.private_key","module.ssh.local_sensitive_file.zip","module.ssh.null_resource.mkdirs"]},{"index_key":"bastion","schema_version":0,"attributes":{"created_at":"2025-11-02T17:14:32.687524Z","hostname":"bastion-01","id":"bd33a447-593f-4d62-a325-225acd891c84","organization_id":"11a40e2e-fff9-49ff-84cf-60f6aff488ed","private_ip_address":"10.0.0.10","public_ip_address":"54.12.34.56","raw":"{\"created_at\":\"2025-11-02T17:14:32.687524Z\",\"hostname\":\"bastion-01\",\"id\":\"bd33a447-593f-4d62-a325-225acd891c84\",\"organization_id\":\"11a40e2e-fff9-49ff-84cf-60f6aff488ed\",\"private_ip_address\":\"10.0.0.10\",\"public_ip_address\":\"54.12.34.56\",\"role\":\"bastion\",\"ssh_key_id\":\"7a7d5424-f311-4c42-b882-0eb41471fbcc\",\"ssh_user\":\"ubuntu\",\"status\":\"pending\",\"updated_at\":\"2025-11-02T17:14:32.687524Z\"}","role":"bastion","ssh_key_id":"7a7d5424-f311-4c42-b882-0eb41471fbcc","ssh_user":"ubuntu","status":"pending","updated_at":"2025-11-02T17:14:32.687524Z"},"sensitive_attributes":[],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.local_file.public_key","module.ssh.local_sensitive_file.private_key","module.ssh.local_sensitive_file.zip","module.ssh.null_resource.mkdirs"]},{"index_key":"manager1","schema_version":0,"attributes":{"created_at":"2025-11-02T17:14:32.664514Z","hostname":"k3s-mgr-01","id":"9b7d4f40-80b9-48fc-a09f-2c9a9a022be9","organization_id":"11a40e2e-fff9-49ff-84cf-60f6aff488ed","private_ip_address":"10.0.1.11","public_ip_address":null,"raw":"{\"created_at\":\"2025-11-02T17:14:32.664514Z\",\"hostname\":\"k3s-mgr-01\",\"id\":\"9b7d4f40-80b9-48fc-a09f-2c9a9a022be9\",\"organization_id\":\"11a40e2e-fff9-49ff-84cf-60f6aff488ed\",\"private_ip_address\":\"10.0.1.11\",\"role\":\"manager\",\"ssh_key_id\":\"59699815-6d51-4e6f-b5b9-037781161bc4\",\"ssh_user\":\"ubuntu\",\"status\":\"pending\",\"updated_at\":\"2025-11-02T17:14:32.664514Z\"}","role":"manager","ssh_key_id":"59699815-6d51-4e6f-b5b9-037781161bc4","ssh_user":"ubuntu","status":"pending","updated_at":"2025-11-02T17:14:32.664514Z"},"sensitive_attributes":[],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.local_file.public_key","module.ssh.local_sensitive_file.private_key","module.ssh.local_sensitive_file.zip","module.ssh.null_resource.mkdirs"]}]},{"module":"module.ssh[\"bastionKey\"]","mode":"data","type":"http","name":"download","provider":"provider[\"registry.opentofu.org/hashicorp/http\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"body":"{\"id\":\"7a7d5424-f311-4c42-b882-0eb41471fbcc\",\"name\":\"Bastion Key\",\"fingerprint\":\"SHA256:rqkycNDOYovxjt8CZkEKOPjrsUHLA5jq0kyahb8iMh4\",\"zip_base64\":\"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAvAAAAaWRfcnNhXzdhN2Q1NDI0LWYzMTEtNGM0Mi1iODgyLTBlYjQxNDcxZmJjYy5wZW1s17USpAC2xvGcp5icuoU0GtwAa9ydDHdpHJ5+a6Y23JOe7Ktf8v+/v8cKomz8cVzmj+XIAeMJf1Qh/vcBdFlWVEZmGUblmFpg7jMaqEGUJRmK4LcIi7WLoPdOfin4oy2Jb86hjS3CGUiHnb2RAYLZ3Mlp9esP+isQEUMzpVv1RH6VvqqNKv0xE7J1oB7uXkctBvdiAUvKzwBv8edn9S4Qb74He8/g7tjPtcj7GuxQWwgNsqXiJ/LbRpAdfn4Fu9liils4vUBanhL7nOdqK7sFYAquOU2lUYPU79Clqhf+WrWj+nJ33IzjzM/OJYRUQZaJ+lov/7Y+kaBHwUU1Uma0IAHSrFlCVq029VQ0a0kSZD5ESXeVkVzkeh+M4tXRgLNsyws/imjFFy5288x8cBZzye6AjdCj+x6RUN5a8LV1jndy9OeaEJZbZkWd32h9vfE7bxD3GlaEKTBEKKQvd+U1FSwLATqVitdBwqTtBrwdznPUU0ISVNW3jkcZgoJXP82PvUnLr4DB8nBPw27RpVADFo0cDARMRNOIkjBqWzo3RZ5sJZva8IL3RcKcnKIWuOVBr2WnTecUtyO1tbOV76KNx6k3A3EDX2g3TS7nEta5LPmWjtZFobV0zsyIRy4lZkiez62l9BoPWVS3Ye9oVbW4URG77HjpAbDmfoEPC2WwszGq+FiVszAcEKu34FjVD3gLb+jWM8fa7Np4V8Qanukr2AoKRTH7+wD7d8n0S3WRfrfLLqCzg1/Q5mwIdEaxZi8H5BJuZB7Uir68yIml2+edSGddn+KYS2AAxv5LmJHJwXIceLcZszEvm1hiVY/VbT4IISxzjH0J9nCGUGh3RmsRYjnncRpgyj0AJgXDpKzRd1u8WcF1tyWWlb3m79zL/BNVhM/nxo5Xh8Y+HGmjza2hRn+xFaNNXZbfwNd6T/KIBIp+oT37ORTpTuCHhVziRJ0mDUqxYRlLeoSwbzyTflpqdOk5F2cF9aSzFQAh6jWwv6/VeNRAWa/bGz4kOqoxnMfsh1xPNLeH2U6Lg49KL4uibitP6VdJ/rHNNV4DaZx3rZtyrYH6QSd9cUL/yrnU/Ch4+MBjMP52JR4gsqybSPP6g608hZvW5YkpEk1EEPjg6YkPx2uVC1UwGNjS5uJbi2kH+Kf+nOKu8KfYxicONkp7lDLSBx6i9M+BrOPE1gXwhXVGUD8q+do/ng5G7eSHiXHSTJFT7obf3+h8tkhL1jJoUygVJt3evIqMiLBPBCgLAWY3G6Ezrfi6208wRLpnPOEiOIuOZLj1FAQhZuTHksry3eCUgh47+v205qJ0KFHjJAbo2mfmYE6CIIidGMVPhw/8m3Phjm6eSFkg9I6LClvXHxq3DC43600vvXYZ2XCDnhIBnZgvc2bPWFm8var/nDJhuBm+069QfdZ6nfu3WMjyN+TZ2NwRhyiRR4nwQYpOUVPzCsylvq7+F+P0aQ3f9h1cEDoy9bcOw3eMNe1NklRdX1CMEoXX5Pv9K9cWGIzhaoLuMGBkFtoU0485+UziELG5tS1fmtsu4PxsBZnfGLH5GDsUzzYlPMU0KizTJ+yU8LxDLyTA85QIg5eDJTATjD7BP85a0PK0xqsnztvD9bKT0/NjT9IuC23BTkZ3GR4Sw+NrOKkCzBN6zgG3rehv2myBVFrHgBw/1EWNxmi8M1dnqGLMWWcIGoqFetytr/LpfiQdZyXrAExURXiqIhQP5KwpQ5/jXsMy159aAatPuOe/FvUdOA/X7s2g8UxoR6PwdLy7V8ooKway9lIF+Ox4eobD3F9WsvSZx0YwkOfTBgkL1huyIV3jYN51YbP0/qKD8cvw9APLe6IBaBSiJuXQe6wGx/XfhVFRZkVq/cwtQWGXdboMcVer9prnXtyw7YENwYqY/pt6AygyMYerknE7Bmm47/yNNTGS1XS+Yy6s3uI913x3KuFZJvjCPjFlfye9xd3aRNMZM1SATsPlDM7m59ui59DSnMSTeyBNNtDXc95JvUQhfuTSaHdKmROsWnEkejv7viERsTMvoCFtSPK2NHDFV8wriQ6EHYHtm425MHSZqOKhjh4KnAu8raCvWk8MGjt126nap15VDUhMM2QElctsBaMVbE3Pw1AKaAO7pD0E/iAc0KxfvV/Zq7gNLOlwuuAJ8851mKe14AVek77glcleMMUpFTd6IshtGN7APjOH3yfoq1sw4/5T/SPMSvN1epfvTfG+V4KvNAAqU8+NC8pMJBOhbda1WbZwlXrlfKVakpdhFNRUjl+MXoTq/H6EpHItMGbU5BwTMZqAXkBMH9qweMJjTk4DatbEGzXMfG5rH3GH0q+0qkfuJgVNU3/36BBltqjnoi0jTLIVoIwNundirI4+WhNEpZcs3E8ZkDSXolhacYWIxwRzRiS6NbFFrI8u6n6ta565Yi/z6MB8fgazbWj29hpMHs4mUHd9ia+HlcrUgtaE4AiUZjokmusrm0EnPcUUe2bkJZD5vi1gm6e7IAzMSXHcWpvEhwOtQe9mCvojRXbjZOpi0kw/R/YyIUJfc2T301jGa+DDY8QTIPq5xMwyy0h2F+/VBSqBVPlRnvahka7l/AOtwIg3srwNpFXho1fDlDkrOO1zvUmbCuCQNLsSRlPyeZyRoUhn2RZ5PeVufzP1SKXcM+FzkGyza4CNea/HFtWMocoGJHt2eAUo3KIutgwnLv9oXIrQppt82dymE3hcqU5eWqiFePlGUkm80A2Jr5uY4BnpcY5G574C1myS7KeLaMgO3ulqQaJGtbpF+6iQ5qnzjuSOGEN1Fldg3Cj0UGxSNeG5j0r22RlbAe/h3D5IlzJ1sduHKuLTIHKj6FN1sWkASYHqvwGb4av9KmTwcc0YVjk/UqbTK+U1FQBGF2plOIpafqJFO2Tsqs+s5vQDti8G6rm6FlhGEQ0vSIPmvryiFEYh/AzKcuSJavmAXfWoDxvD8iT7Jzqp4de06pfPwVcUjjSiihJkCZT2UjKfnOKFq01aDoPCa9sbBC1mgPH5dhL3XjD9jHcne69FMNFv/KKDGXRlWRwGpGvf60kWMY9TeYrD0jI+CUje8uRKOwOMAekG0O8HYXvZW3hizJPjIEJqzy4IxteO46ujG05MT1PnlFH0G2geF3J78wo6eGsXkLloDJm5PLspRFSNWdd9HwW11tEgsygJJiB5mnssBEX03o5AqmwtZ0eLIrHcJz/KDDgfq2Ek2K04/lBmKhd+B/IxvlYv5MkrLRA/+uwxN7lKn0ghgUZvpncouaVz5v8P/EsOweD/d4r8JwAA//9QSwcI1HK4R6UJAACrDAAAUEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAvAAAAaWRfcnNhXzdhN2Q1NDI0LWYzMTEtNGM0Mi1iODgyLTBlYjQxNDcxZmJjYy5wdWIUx8kSc0gAAOBX+e9qCpFYDn/VNIktgiBBbsROdFtaa08/NcdvWZp/5iX7AwAAquAemcbT7+n2P6/gCVQAgAaeV0MpE/pK62+nVJHLf07YrVhf/6FZnDn56xlMbNcsMpL1ZjReqsa1KOByDa9VhoRX3n2Wn7fsfff4BQzglQlqVUz9857xnk97zvGbl7l09yTcuxIygd3ugR/49/ddaJHFbqb2VlM8wk0BxQmmHj1m7P/kV6AZe0fuvbULseWTLCCPO1C2W1gkWoNgmW4yjGMXIjebrMmngcjr9mJZUsnZIyxYWZY+T/xsJxr0FWuNfYq+v+4jbIsITFhDxkaP+YOdbTtfKySdXJkfr3gzzmDw1DLpb4b1COdjsnI/OHGiskZi/SGF5h1wr0wjf6ctdSQXEMvVjwxZEtsQ+cJS+V1urpfG7SE7D06p6jFi4bg+W1fSCu5myscOc03aolW1X66atU0ZkWB4K3KstmnHKsmDylKhkfKHhOgiXu4yrznZwKGX+SnM7iy9lg1J8KSCkiF9W4ABl3Z+DU991ejHQcfQ8i+eZZSEWMPdXzB2QR/3OBNlaiTzLWG489kmdq+bQO5upWg7ObNeDGXh29Sep05uW4Y6hF6H1Ez3a+CJFkSCmeadqra/SAR39I6ewSXm9C4dokYhZhQk0+qNY8OEyc8Jj4w+NREbHM51dlozZX4EYMyVojbmkouH5QDIiUNeFd2Ex2dvzAzGnLMubw1eTBIQmXH7QqyguoVeV/rT1b9EtyPy9++fokQDpP9meIX1gMv/AgAA//9QSwcIHlWcN1UCAADkAgAAUEsBAhQAFAAIAAgAAAAAANRyuEelCQAAqwwAAC8AAAAAAAAAAAAAAAAAAAAAAGlkX3JzYV83YTdkNTQyNC1mMzExLTRjNDItYjg4Mi0wZWI0MTQ3MWZiY2MucGVtUEsBAhQAFAAIAAgAAAAAAB5VnDdVAgAA5AIAAC8AAAAAAAAAAAAAAAAAAgoAAGlkX3JzYV83YTdkNTQyNC1mMzExLTRjNDItYjg4Mi0wZWI0MTQ3MWZiY2MucHViUEsFBgAAAAACAAIAugAAALQMAAAAAA==\",\"filenames\":[\"id_rsa_7a7d5424-f311-4c42-b882-0eb41471fbcc.zip\",\"id_rsa_7a7d5424-f311-4c42-b882-0eb41471fbcc.pem\",\"id_rsa_7a7d5424-f311-4c42-b882-0eb41471fbcc.pub\"]}\n","ca_cert_pem":null,"client_cert_pem":null,"client_key_pem":null,"id":"http://localhost:8080/api/v1/ssh/7a7d5424-f311-4c42-b882-0eb41471fbcc/download?part=both\u0026mode=json","insecure":null,"method":null,"request_body":null,"request_headers":{"Accept":"application/json","X-ORG-KEY":"org_lnJwmyyWH7JC-JgZo5v3Kw","X-ORG-SECRET":"fqd9yebGMfK6h5HSgWn4sXrwr9xlFbvbIYtNylRElMQ"},"request_timeout_ms":null,"response_body":"{\"id\":\"7a7d5424-f311-4c42-b882-0eb41471fbcc\",\"name\":\"Bastion Key\",\"fingerprint\":\"SHA256:rqkycNDOYovxjt8CZkEKOPjrsUHLA5jq0kyahb8iMh4\",\"zip_base64\":\"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\",\"filenames\":[\"id_rsa_7a7d5424-f311-4c42-b882-0eb41471fbcc.zip\",\"id_rsa_7a7d5424-f311-4c42-b882-0eb41471fbcc.pem\",\"id_rsa_7a7d5424-f311-4c42-b882-0eb41471fbcc.pub\"]}\n","response_body_base64":"eyJpZCI6IjdhN2Q1NDI0LWYzMTEtNGM0Mi1iODgyLTBlYjQxNDcxZmJjYyIsIm5hbWUiOiJCYXN0aW9uIEtleSIsImZpbmdlcnByaW50IjoiU0hBMjU2OnJxa3ljTkRPWW92eGp0OENaa0VLT1BqcnNVSExBNWpxMGt5YWhiOGlNaDQiLCJ6aXBfYmFzZTY0IjoiVUVzREJCUUFDQUFJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF2QUFBQWFXUmZjbk5oWHpkaE4yUTFOREkwTFdZek1URXROR00wTWkxaU9EZ3lMVEJsWWpReE5EY3habUpqWXk1d1pXMXMxN1VTcEFDMnh2R2NwNWljdW9VMEd0d0FhOXlkREhkcEhKNSthNlkyM0pPZTdLdGY4disvdjhjS29tejhjVnptaitYSUFlTUpmMVFoL3ZjQmRGbFdWRVptR1VibG1GcGc3ak1hcUVHVUpSbUs0TGNJaTdXTG9QZE9maW40b3kySmI4NmhqUzNDR1VpSG5iMlJBWUxaM01scDllc1AraXNRRVVNenBWdjFSSDZWdnFxTkt2MHhFN0oxb0I3dVhrY3RCdmRpQVV2S3p3QnY4ZWRuOVM0UWI3NEhlOC9nN3RqUHRjajdHdXhRV3dnTnNxWGlKL0xiUnBBZGZuNEZ1OWxpaWxzNHZVQmFuaEw3bk9kcUs3c0ZZQXF1T1UybFVZUFU3OUNscWhmK1dyV2orbkozM0l6anpNL09KWVJVUVphSitsb3YvN1kra2FCSHdVVTFVbWEwSUFIU3JGbENWcTAyOVZRMGEwa1NaRDVFU1hlVmtWemtlaCtNNHRYUmdMTnN5d3MvaW1qRkZ5NTI4OHg4Y0JaenllNkFqZENqK3g2UlVONWE4TFYxam5keTlPZWFFSlpiWmtXZDMyaDl2ZkU3YnhEM0dsYUVLVEJFS0tRdmQrVTFGU3dMQVRxVml0ZEJ3cVR0QnJ3ZHpuUFVVMElTVk5XM2prY1pnb0pYUDgyUHZVbkxyNERCOG5CUHcyN1JwVkFERm8wY0RBUk1STk9Ja2pCcVd6bzNSWjVzSlp2YThJTDNSY0tjbktJV3VPVkJyMlduVGVjVXR5TzF0Yk9WNzZLTng2azNBM0VEWDJnM1RTN25FdGE1TFBtV2p0WkZvYlYwenN5SVJ5NGxaa2llejYybDlCb1BXVlMzWWU5b1ZiVzRVUkc3N0hqcEFiRG1mb0VQQzJXd3N6R3ErRmlWc3pBY0VLdTM0RmpWRDNnTGIraldNOGZhN05wNFY4UWFudWtyMkFvS1JUSDcrd0Q3ZDhuMFMzV1JmcmZMTHFDemcxL1E1bXdJZEVheFppOEg1Qkp1WkI3VWlyNjh5SW1sMitlZFNHZGRuK0tZUzJBQXh2NUxtSkhKd1hJY2VMY1pzekV2bTFoaVZZL1ZiVDRJSVN4empIMEo5bkNHVUdoM1Jtc1JZam5uY1JwZ3lqMEFKZ1hEcEt6UmQxdThXY0YxdHlXV2xiM203OXpML0JOVmhNL254bzVYaDhZK0hHbWp6YTJoUm4reEZhTk5YWmJmd05kNlQvS0lCSXArb1QzN09SVHBUdUNIaFZ6aVJKMG1EVXF4WVJsTGVvU3dienlUZmxwcWRPazVGMmNGOWFTekZRQWg2ald3djYvVmVOUkFXYS9iR3o0a09xb3huTWZzaDF4UE5MZUgyVTZMZzQ5S0w0dWliaXRQNlZkSi9ySE5OVjREYVp4M3JadHlyWUg2UVNkOWNVTC95cm5VL0NoNCtNQmpNUDUySlI0Z3NxeWJTUFA2ZzYwOGhadlc1WWtwRWsxRUVQamc2WWtQeDJ1VkMxVXdHTmpTNXVKYmkya0grS2Yrbk9LdThLZll4aWNPTmtwN2xETFNCeDZpOU0rQnJPUEUxZ1h3aFhWR1VEOHErZG8vbmc1RzdlU0hpWEhTVEpGVDdvYmYzK2g4dGtoTDFqSm9VeWdWSnQzZXZJcU1pTEJQQkNnTEFXWTNHNkV6cmZpNjIwOHdSTHBuUE9FaU9JdU9aTGoxRkFRaFp1VEhrc3J5M2VDVWdoNDcrdjIwNXFKMEtGSGpKQWJvMm1mbVlFNkNJSWlkR01WUGh3LzhtM1Boam02ZVNGa2c5STZMQ2x2WEh4cTNEQzQzNjAwdnZYWVoyWENEbmhJQm5aZ3ZjMmJQV0ZtOHZhci9uREpodUJtKzA2OVFmZFo2bmZ1M1dNanlOK1RaMk53Umh5aVJSNG53UVlwT1VWUHpDc3lsdnE3K0YrUDBhUTNmOWgxY0VEb3k5YmNPdzNlTU5lMU5rbFJkWDFDTUVvWFg1UHY5SzljV0dJemhhb0x1TUdCa0Z0b1UwNDg1K1V6aUVMRzV0UzFmbXRzdTRQeHNCWm5mR0xINUdEc1V6ellsUE1VMEtpelRKK3lVOEx4REx5VEE4NVFJZzVlREpUQVRqRDdCUDg1YTBQSzB4cXNuenR2RDliS1QwL05qVDlJdUMyM0JUa1ozR1I0U3crTnJPS2tDekJONnpnRzNyZWh2Mm15QlZGckhnQncvMUVXTnhtaThNMWRucUdMTVdXY0lHb3FGZXR5dHIvTHBmaVFkWnlYckFFeFVSWGlxSWhRUDVLd3BRNS9qWHNNeTE1OWFBYXRQdU9lL0Z2VWRPQS9YN3MyZzhVeG9SNlB3ZEx5N1Y4b29Ld2F5OWxJRitPeDRlb2JEM0Y5V3N2U1p4MFl3a09mVEJna0wxaHV5SVYzallONTFZYlAwL3FLRDhjdnc5QVBMZTZJQmFCU2lKdVhRZTZ3R3gvWGZoVkZSWmtWcS9jd3RRV0dYZGJvTWNWZXI5cHJuWHR5dzdZRU53WXFZL3B0NkF5Z3lNWWVya25FN0JtbTQ3L3lOTlRHUzFYUytZeTZzM3VJOTEzeDNLdUZaSnZqQ1BqRmxmeWU5eGQzYVJOTVpNMVNBVHNQbERNN201OXVpNTlEU25NU1RleUJOTnREWGM5NUp2VVFoZnVUU2FIZEttUk9zV25Fa2Vqdjd2aUVSc1RNdm9DRnRTUEsyTkhERlY4d3JpUTZFSFlIdG00MjVNSFNacU9LaGpoNEtuQXU4cmFDdldrOE1HanQxMjZuYXAxNVZEVWhNTTJRRWxjdHNCYU1WYkUzUHcxQUthQU83cEQwRS9pQWMwS3hmdlYvWnE3Z05MT2x3dXVBSjg4NTFtS2UxNEFWZWs3N2dsY2xlTU1VcEZUZDZJc2h0R043QVBqT0gzeWZvcTFzdzQvNVQvU1BNU3ZOMWVwZnZUZkcrVjRLdk5BQXFVOCtOQzhwTUpCT2hiZGExV2Jad2xYcmxmS1Zha3BkaEZOUlVqbCtNWG9UcS9INkVwSEl0TUdiVTVCd1RNWnFBWGtCTUg5cXdlTUpqVGs0RGF0YkVHelhNZkc1ckgzR0gwcSswcWtmdUpnVk5VMy8zNkJCbHRxam5vaTBqVExJVm9Jd051bmRpckk0K1doTkVwWmNzM0U4WmtEU1hvbGhhY1lXSXh3UnpSaVM2TmJGRnJJOHU2bjZ0YTU2NVlpL3o2TUI4ZmdhemJXajI5aHBNSHM0bVVIZDlpYStIbGNyVWd0YUU0QWlVWmpva211c3JtMEVuUGNVVWUyYmtKWkQ1dmkxZ202ZTdJQXpNU1hIY1dwdkVod090UWU5bUN2b2pSWGJqWk9waTBrdy9SL1l5SVVKZmMyVDMwMWpHYStERFk4UVRJUHE1eE13eXkwaDJGKy9WQlNxQlZQbFJudmFoa2E3bC9BT3R3SWczc3J3TnBGWGhvMWZEbERrck9PMXp2VW1iQ3VDUU5Mc1NSbFB5ZVp5Um9VaG4yUlo1UGVWdWZ6UDFTS1hjTStGemtHeXphNENOZWEvSEZ0V01vY29HSkh0MmVBVW8zS0l1dGd3bkx2OW9YSXJRcHB0ODJkeW1FM2hjcVU1ZVdxaUZlUGxHVWttODBBMkpyNXVZNEJucGNZNUc1NzRDMW15UzdLZUxhTWdPM3VscVFhSkd0YnBGKzZpUTVxbnpqdVNPR0VOMUZsZGczQ2owVUd4U05lRzVqMHIyMlJsYkFlL2gzRDVJbHpKMXNkdUhLdUxUSUhLajZGTjFzV2tBU1lIcXZ3R2I0YXY5S21Ud2NjMFlWamsvVXFiVEsrVTFGUUJHRjJwbE9JcGFmcUpGTzJUc3FzK3M1dlFEdGk4RzZybTZGbGhHRVEwdlNJUG12cnlpRkVZaC9BektjdVNKYXZtQVhmV29EeHZEOGlUN0p6cXA0ZGUwNnBmUHdWY1VqalNpaWhKa0NaVDJVaktmbk9LRnEwMWFEb1BDYTlzYkJDMW1nUEg1ZGhMM1hqRDlqSGNuZTY5Rk1ORnYvS0tER1hSbFdSd0dwR3ZmNjBrV01ZOVRlWXJEMGpJK0NVamU4dVJLT3dPTUFla0cwTzhIWVh2WlczaGl6SlBqSUVKcXp5NEl4dGVPNDZ1akcwNU1UMVBubEZIMEcyZ2VGM0o3OHdvNmVHc1hrTGxvREptNVBMc3BSRlNOV2RkOUh3VzExdEVnc3lnSkppQjVtbnNzQkVYMDNvNUFxbXd0WjBlTElySGNKei9LRERnZnEyRWsySzA0L2xCbUtoZCtCL0l4dmxZdjVNa3JMUkEvK3V3eE43bEtuMGdoZ1VadnBuY291YVZ6NXY4UC9Fc093ZUQvZDRyOEp3QUEvLzlRU3djSTFISzRSNlVKQUFDckRBQUFVRXNEQkJRQUNBQUlBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQXZBQUFBYVdSZmNuTmhYemRoTjJRMU5ESTBMV1l6TVRFdE5HTTBNaTFpT0RneUxUQmxZalF4TkRjeFptSmpZeTV3ZFdJVXg4a1NjMGdBQU9CWCtlOXFDcEZZRG4vVk5Ja3RnaUJCYnNST2RGdGFhMDgvTmNkdldacC81aVg3QXdBQXF1QWVtY2JUNytuMlA2L2dDVlFBZ0FhZVYwTXBFL3BLNjIrblZKSExmMDdZclZoZi82RlpuRG41NnhsTWJOY3NNcEwxWmpSZXFzYTFLT0J5RGE5VmhvUlgzbjJXbjdmc2ZmZjRCUXpnbFFscVZVejk4NTd4bms5N3p2R2JsN2wwOXlUY3V4SXlnZDN1Z1IvNDkvZGRhSkhGYnFiMlZsTTh3azBCeFFtbUhqMW03UC9rVjZBWmUwZnV2YlVMc2VXVExDQ1BPMUMyVzFna1dvTmdtVzR5akdNWElqZWJyTW1uZ2NqcjltSlpVc25aSXl4WVdaWStUL3hzSnhyMEZXdU5mWXErdis0amJJc0lURmhEeGthUCtZT2RiVHRmS3lTZFhKa2ZyM2d6em1EdzFETHBiNGIxQ09kanNuSS9PSEdpc2taaS9TR0Y1aDF3cjB3amY2Y3RkU1FYRU12Vmp3eFpFdHNRK2NKUytWMXVycGZHN1NFN0QwNnA2akZpNGJnK1cxZlNDdTVteXNjT2MwM2FvbFcxWDY2YXRVMFprV0I0SzNLc3RtbkhLc21EeWxLaGtmS0hoT2dpWHU0eXJ6blp3S0dYK1NuTTdpeTlsZzFKOEtTQ2tpRjlXNEFCbDNaK0RVOTkxZWpIUWNmUThpK2VaWlNFV01QZFh6QjJRUi8zT0JObGFpVHpMV0c0ODlrbWRxK2JRTzV1cFdnN09iTmVER1hoMjlTZXAwNXVXNFk2aEY2SDFFejNhK0NKRmtTQ21lYWRxcmEvU0FSMzlJNmV3U1htOUM0ZG9rWWhaaFFrMCtxTlk4T0V5YzhKajR3K05SRWJITTUxZGxvelpYNEVZTXlWb2pibWtvdUg1UURJaVVOZUZkMkV4MmR2ekF6R25MTXVidzFlVEJJUW1YSDdRcXlndW9WZVYvclQxYjlFdHlQeTkrK2Zva1FEcFA5bWVJWDFnTXYvQWdBQS8vOVFTd2NJSGxXY04xVUNBQURrQWdBQVVFc0JBaFFBRkFBSUFBZ0FBQUFBQU5SeXVFZWxDUUFBcXd3QUFDOEFBQUFBQUFBQUFBQUFBQUFBQUFBQUFHbGtYM0p6WVY4M1lUZGtOVFF5TkMxbU16RXhMVFJqTkRJdFlqZzRNaTB3WldJME1UUTNNV1ppWTJNdWNHVnRVRXNCQWhRQUZBQUlBQWdBQUFBQUFCNVZuRGRWQWdBQTVBSUFBQzhBQUFBQUFBQUFBQUFBQUFBQUFnb0FBR2xrWDNKellWODNZVGRrTlRReU5DMW1NekV4TFRSak5ESXRZamc0TWkwd1pXSTBNVFEzTVdaaVkyTXVjSFZpVUVzRkJnQUFBQUFDQUFJQXVnQUFBTFFNQUFBQUFBPT0iLCJmaWxlbmFtZXMiOlsiaWRfcnNhXzdhN2Q1NDI0LWYzMTEtNGM0Mi1iODgyLTBlYjQxNDcxZmJjYy56aXAiLCJpZF9yc2FfN2E3ZDU0MjQtZjMxMS00YzQyLWI4ODItMGViNDE0NzFmYmNjLnBlbSIsImlkX3JzYV83YTdkNTQyNC1mMzExLTRjNDItYjg4Mi0wZWI0MTQ3MWZiY2MucHViIl19Cg==","response_headers":{"Content-Security-Policy":"default-src 'self'; base-uri 'self'; form-action 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:5173; style-src 'self' 'unsafe-inline' http://localhost:5173 https://fonts.googleapis.com; img-src 'self' data: blob:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' http://localhost:5173 ws://localhost:5173 ws://localhost:8080; frame-ancestors 'none'","Content-Type":"application/json","Date":"Sun, 02 Nov 2025 17:14:32 GMT","Permissions-Policy":"geolocation=(), camera=(), microphone=(), interest-cohort=()","Referrer-Policy":"strict-origin-when-cross-origin","Vary":"Origin","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-Ratelimit-Limit":"100","X-Ratelimit-Remaining":"96","X-Ratelimit-Reset":"1762103700"},"retry":null,"status_code":200,"url":"http://localhost:8080/api/v1/ssh/7a7d5424-f311-4c42-b882-0eb41471fbcc/download?part=both\u0026mode=json"},"sensitive_attributes":[[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-KEY","type":"string"}}],[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-SECRET","type":"string"}}]]}]},{"module":"module.ssh[\"bastionKey\"]","mode":"managed","type":"autoglue_ssh_key","name":"this","provider":"provider[\"glueops/autoglue/autoglue\"]","instances":[{"schema_version":0,"attributes":{"bits":4096,"comment":"deploy@autoglue","created_at":"2025-11-02T17:14:32Z","fingerprint":"SHA256:rqkycNDOYovxjt8CZkEKOPjrsUHLA5jq0kyahb8iMh4","id":"7a7d5424-f311-4c42-b882-0eb41471fbcc","name":"Bastion Key","private_key_pem":null,"public_key":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDG9eXyUYgcj9fTN1Z2uNf/PFmpr6r08cOG+WJg/pGXtEGhOYBWg63uetSDfap3UbjZsmOsxkjMmR+A19qoCfWyP4xa1OPyk0LPhUHsjKXSxjeo+RJixRPRPKVK3ipI/vHCVBYunov9Ad2oYOyzruPm8URCGxjwKkIx3WIPwaRwMKA9vESdXChpoeYv8oWWNopNaqIqPyR61FJsII7e0Jnod/887ZQuQiqyRkf/InkYpcmjZ3vs6AHogo+JpMrZuLvv4Dfp72N81nDuvG4AlOBeXkEGIMSrzqIbPR2069tT6gZwdCOzoxfHGbVYiyL7NAwINFzapI7/hw85/y8VevNOYWiz8LM09fgnT/ontQiN7Cd0EH8zxobC7vTtBJUNBaiheTwRlV98WBiYj/9XMy87dCwemp3T565K81CLal0pUHZdHj47Usvp7o2BAe+wkidAlueJbDS2kfhFzzynSIP5OIGewwIlKPsuuNAkWkua68yGXrEX+044JwJkFHA8jEe6JLb+t5G9s1iYJrqj8ii+yLwyDlYHYxDRO6Iop3HYbjBBimT6AKpVTQR5W0FjYlTh9wHTRXqtOnnh+SXmLSzayQC6uG0ubF/qta9rMRAnb9dgGre0WlszApLWS1B6NX1u4OnaG+HrajbiG16XXATHWiUp/3BNdFgfFQNFcwFJTw== deploy@autoglue","type":"rsa","updated_at":"2025-11-02T17:14:32Z"},"sensitive_attributes":[[{"type":"get_attr","value":"private_key_pem"}]]}]},{"module":"module.ssh[\"bastionKey\"]","mode":"managed","type":"local_sensitive_file","name":"zip","provider":"provider[\"registry.opentofu.org/hashicorp/local\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"content":null,"content_base64":"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","content_base64sha256":"+APyjIi9FA3K2uwJMeh5XyGUcOHJ0IHxkDHF3zlSaxU=","content_base64sha512":"/3/Ji+7JccxiDRMWtvkaGATL2u8G42gvT2J/uMvtctJP1gCPI3xXaaFkKPfC0qbAjA3NQO1pSAGEszQ9Mo+nCQ==","content_md5":"f204e849932a5f34f829bac4c6473027","content_sha1":"733a0b645564c212a844746743839afafc8adc0e","content_sha256":"f803f28c88bd140dcadaec0931e8795f219470e1c9d081f19031c5df39526b15","content_sha512":"ff7fc98beec971cc620d1316b6f91a1804cbdaef06e3682f4f627fb8cbed72d24fd6008f237c5769a16428f7c2d2a6c08c0dcd40ed69480184b3343d328fa709","directory_permission":"0700","file_permission":"0700","filename":"out/bastionKey/id_rsa_7a7d5424-f311-4c42-b882-0eb41471fbcc.zip","id":"733a0b645564c212a844746743839afafc8adc0e","source":null},"sensitive_attributes":[[{"type":"get_attr","value":"content"}],[{"type":"get_attr","value":"content_base64"}]],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.null_resource.mkdirs"]}]},{"module":"module.ssh[\"bastionKey\"]","mode":"managed","type":"null_resource","name":"mkdirs","provider":"provider[\"registry.opentofu.org/hashicorp/null\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"id":"3523105084924039922","triggers":null},"sensitive_attributes":[]}]},{"module":"module.ssh[\"clusterKey\"]","mode":"data","type":"http","name":"download","provider":"provider[\"registry.opentofu.org/hashicorp/http\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"body":"{\"id\":\"59699815-6d51-4e6f-b5b9-037781161bc4\",\"name\":\"Cluster Key\",\"fingerprint\":\"SHA256:bTBKjZJInFjUkrQJiZhrhfKqkfl5Yusi2hQzqFsfxfM\",\"zip_base64\":\"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV81OTY5OTgxNS02ZDUxLTRlNmYtYjViOS0wMzc3ODExNjFiYzQucGVt0gUBJ1d3Tz+FgCDPMMcQVwVv10iwKJevs4mzY6BjuVNgpIu3UVi5k7Onq6dTpXuyfpqJf5F3gbmxaUVWWEpkZkl2VYlhkFdoZo6XX3CYo2uSabJ2ZLgJF9gYVz8XTKMBAQAA//9QSwcInaA4GmUAAAB3AAAAUEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV81OTY5OTgxNS02ZDUxLTRlNmYtYjViOS0wMzc3ODExNjFiYzQucHViKi7O0E1NMTI1NbRUcHR0dHQ29qtKdDbMiXLxNPQLcTUFiXl6mvm6F7s4OZYGlyUmFUdF5JQEh2RXJSYZm1r4VGSaF/omuRUVRXpl6CflKyQlFpdk5uc5JJaW5KfnlKYCAgAA//9QSwcIdPU5DGUAAABhAAAAUEsBAhQAFAAIAAgAAAAAAJ2gOBplAAAAdwAAADMAAAAAAAAAAAAAAAAAAAAAAGlkX2VkMjU1MTlfNTk2OTk4MTUtNmQ1MS00ZTZmLWI1YjktMDM3NzgxMTYxYmM0LnBlbVBLAQIUABQACAAIAAAAAAB09TkMZQAAAGEAAAAzAAAAAAAAAAAAAAAAAMYAAABpZF9lZDI1NTE5XzU5Njk5ODE1LTZkNTEtNGU2Zi1iNWI5LTAzNzc4MTE2MWJjNC5wdWJQSwUGAAAAAAIAAgDCAAAAjAEAAAAA\",\"filenames\":[\"id_ed25519_59699815-6d51-4e6f-b5b9-037781161bc4.zip\",\"id_ed25519_59699815-6d51-4e6f-b5b9-037781161bc4.pem\",\"id_ed25519_59699815-6d51-4e6f-b5b9-037781161bc4.pub\"]}\n","ca_cert_pem":null,"client_cert_pem":null,"client_key_pem":null,"id":"http://localhost:8080/api/v1/ssh/59699815-6d51-4e6f-b5b9-037781161bc4/download?part=both\u0026mode=json","insecure":null,"method":null,"request_body":null,"request_headers":{"Accept":"application/json","X-ORG-KEY":"org_lnJwmyyWH7JC-JgZo5v3Kw","X-ORG-SECRET":"fqd9yebGMfK6h5HSgWn4sXrwr9xlFbvbIYtNylRElMQ"},"request_timeout_ms":null,"response_body":"{\"id\":\"59699815-6d51-4e6f-b5b9-037781161bc4\",\"name\":\"Cluster Key\",\"fingerprint\":\"SHA256:bTBKjZJInFjUkrQJiZhrhfKqkfl5Yusi2hQzqFsfxfM\",\"zip_base64\":\"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV81OTY5OTgxNS02ZDUxLTRlNmYtYjViOS0wMzc3ODExNjFiYzQucGVt0gUBJ1d3Tz+FgCDPMMcQVwVv10iwKJevs4mzY6BjuVNgpIu3UVi5k7Onq6dTpXuyfpqJf5F3gbmxaUVWWEpkZkl2VYlhkFdoZo6XX3CYo2uSabJ2ZLgJF9gYVz8XTKMBAQAA//9QSwcInaA4GmUAAAB3AAAAUEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV81OTY5OTgxNS02ZDUxLTRlNmYtYjViOS0wMzc3ODExNjFiYzQucHViKi7O0E1NMTI1NbRUcHR0dHQ29qtKdDbMiXLxNPQLcTUFiXl6mvm6F7s4OZYGlyUmFUdF5JQEh2RXJSYZm1r4VGSaF/omuRUVRXpl6CflKyQlFpdk5uc5JJaW5KfnlKYCAgAA//9QSwcIdPU5DGUAAABhAAAAUEsBAhQAFAAIAAgAAAAAAJ2gOBplAAAAdwAAADMAAAAAAAAAAAAAAAAAAAAAAGlkX2VkMjU1MTlfNTk2OTk4MTUtNmQ1MS00ZTZmLWI1YjktMDM3NzgxMTYxYmM0LnBlbVBLAQIUABQACAAIAAAAAAB09TkMZQAAAGEAAAAzAAAAAAAAAAAAAAAAAMYAAABpZF9lZDI1NTE5XzU5Njk5ODE1LTZkNTEtNGU2Zi1iNWI5LTAzNzc4MTE2MWJjNC5wdWJQSwUGAAAAAAIAAgDCAAAAjAEAAAAA\",\"filenames\":[\"id_ed25519_59699815-6d51-4e6f-b5b9-037781161bc4.zip\",\"id_ed25519_59699815-6d51-4e6f-b5b9-037781161bc4.pem\",\"id_ed25519_59699815-6d51-4e6f-b5b9-037781161bc4.pub\"]}\n","response_body_base64":"eyJpZCI6IjU5Njk5ODE1LTZkNTEtNGU2Zi1iNWI5LTAzNzc4MTE2MWJjNCIsIm5hbWUiOiJDbHVzdGVyIEtleSIsImZpbmdlcnByaW50IjoiU0hBMjU2OmJUQktqWkpJbkZqVWtyUUppWmhyaGZLcWtmbDVZdXNpMmhRenFGc2Z4Zk0iLCJ6aXBfYmFzZTY0IjoiVUVzREJCUUFDQUFJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF6QUFBQWFXUmZaV1F5TlRVeE9WODFPVFk1T1RneE5TMDJaRFV4TFRSbE5tWXRZalZpT1Mwd016YzNPREV4TmpGaVl6UXVjR1Z0MGdVQkoxZDNUeitGZ0NEUE1NY1FWd1Z2MTBpd0tKZXZzNG16WTZCanVWTmdwSXUzVVZpNWs3T25xNmRUcFh1eWZwcUpmNUYzZ2JteGFVVldXRXBrWmtsMlZZbGhrRmRvWm82WFgzQ1lvMnVTYWJKMlpMZ0pGOWdZVno4WFRLTUJBUUFBLy85UVN3Y0luYUE0R21VQUFBQjNBQUFBVUVzREJCUUFDQUFJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF6QUFBQWFXUmZaV1F5TlRVeE9WODFPVFk1T1RneE5TMDJaRFV4TFRSbE5tWXRZalZpT1Mwd016YzNPREV4TmpGaVl6UXVjSFZpS2k3TzBFMU5NVEkxTmJSVWNIUjBkSFEyOXF0S2REYk1pWEx4TlBRTGNUVUZpWGw2bXZtNkY3czRPWllHbHlVbUZVZEY1SlFFaDJSWEpTWVptMXI0VkdTYUYvb211UlVWUlhwbDZDZmxLeVFsRnBkazV1YzVKSmFXNUtmbmxLWUNBZ0FBLy85UVN3Y0lkUFU1REdVQUFBQmhBQUFBVUVzQkFoUUFGQUFJQUFnQUFBQUFBSjJnT0JwbEFBQUFkd0FBQURNQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUdsa1gyVmtNalUxTVRsZk5UazJPVGs0TVRVdE5tUTFNUzAwWlRabUxXSTFZamt0TURNM056Z3hNVFl4WW1NMExuQmxiVkJMQVFJVUFCUUFDQUFJQUFBQUFBQjA5VGtNWlFBQUFHRUFBQUF6QUFBQUFBQUFBQUFBQUFBQUFNWUFBQUJwWkY5bFpESTFOVEU1WHpVNU5qazVPREUxTFRaa05URXROR1UyWmkxaU5XSTVMVEF6TnpjNE1URTJNV0pqTkM1d2RXSlFTd1VHQUFBQUFBSUFBZ0RDQUFBQWpBRUFBQUFBIiwiZmlsZW5hbWVzIjpbImlkX2VkMjU1MTlfNTk2OTk4MTUtNmQ1MS00ZTZmLWI1YjktMDM3NzgxMTYxYmM0LnppcCIsImlkX2VkMjU1MTlfNTk2OTk4MTUtNmQ1MS00ZTZmLWI1YjktMDM3NzgxMTYxYmM0LnBlbSIsImlkX2VkMjU1MTlfNTk2OTk4MTUtNmQ1MS00ZTZmLWI1YjktMDM3NzgxMTYxYmM0LnB1YiJdfQo=","response_headers":{"Content-Length":"1142","Content-Security-Policy":"default-src 'self'; base-uri 'self'; form-action 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:5173; style-src 'self' 'unsafe-inline' http://localhost:5173 https://fonts.googleapis.com; img-src 'self' data: blob:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' http://localhost:5173 ws://localhost:5173 ws://localhost:8080; frame-ancestors 'none'","Content-Type":"application/json","Date":"Sun, 02 Nov 2025 17:14:32 GMT","Permissions-Policy":"geolocation=(), camera=(), microphone=(), interest-cohort=()","Referrer-Policy":"strict-origin-when-cross-origin","Vary":"Origin","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-Ratelimit-Limit":"100","X-Ratelimit-Remaining":"97","X-Ratelimit-Reset":"1762103700"},"retry":null,"status_code":200,"url":"http://localhost:8080/api/v1/ssh/59699815-6d51-4e6f-b5b9-037781161bc4/download?part=both\u0026mode=json"},"sensitive_attributes":[[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-KEY","type":"string"}}],[{"type":"get_attr","value":"request_headers"},{"type":"index","value":{"value":"X-ORG-SECRET","type":"string"}}]]}]},{"module":"module.ssh[\"clusterKey\"]","mode":"managed","type":"autoglue_ssh_key","name":"this","provider":"provider[\"glueops/autoglue/autoglue\"]","instances":[{"schema_version":0,"attributes":{"bits":null,"comment":"bastion@autoglue","created_at":"2025-11-02T17:14:32Z","fingerprint":"SHA256:bTBKjZJInFjUkrQJiZhrhfKqkfl5Yusi2hQzqFsfxfM","id":"59699815-6d51-4e6f-b5b9-037781161bc4","name":"Cluster Key","private_key_pem":null,"public_key":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6MGsDBAuSvabsZXltSTkzab358Lxi7qMbFrrYJh/bo bastion@autoglue","type":"ed25519","updated_at":"2025-11-02T17:14:32Z"},"sensitive_attributes":[[{"type":"get_attr","value":"private_key_pem"}]]}]},{"module":"module.ssh[\"clusterKey\"]","mode":"managed","type":"local_sensitive_file","name":"zip","provider":"provider[\"registry.opentofu.org/hashicorp/local\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"content":null,"content_base64":"UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV81OTY5OTgxNS02ZDUxLTRlNmYtYjViOS0wMzc3ODExNjFiYzQucGVt0gUBJ1d3Tz+FgCDPMMcQVwVv10iwKJevs4mzY6BjuVNgpIu3UVi5k7Onq6dTpXuyfpqJf5F3gbmxaUVWWEpkZkl2VYlhkFdoZo6XX3CYo2uSabJ2ZLgJF9gYVz8XTKMBAQAA//9QSwcInaA4GmUAAAB3AAAAUEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAzAAAAaWRfZWQyNTUxOV81OTY5OTgxNS02ZDUxLTRlNmYtYjViOS0wMzc3ODExNjFiYzQucHViKi7O0E1NMTI1NbRUcHR0dHQ29qtKdDbMiXLxNPQLcTUFiXl6mvm6F7s4OZYGlyUmFUdF5JQEh2RXJSYZm1r4VGSaF/omuRUVRXpl6CflKyQlFpdk5uc5JJaW5KfnlKYCAgAA//9QSwcIdPU5DGUAAABhAAAAUEsBAhQAFAAIAAgAAAAAAJ2gOBplAAAAdwAAADMAAAAAAAAAAAAAAAAAAAAAAGlkX2VkMjU1MTlfNTk2OTk4MTUtNmQ1MS00ZTZmLWI1YjktMDM3NzgxMTYxYmM0LnBlbVBLAQIUABQACAAIAAAAAAB09TkMZQAAAGEAAAAzAAAAAAAAAAAAAAAAAMYAAABpZF9lZDI1NTE5XzU5Njk5ODE1LTZkNTEtNGU2Zi1iNWI5LTAzNzc4MTE2MWJjNC5wdWJQSwUGAAAAAAIAAgDCAAAAjAEAAAAA","content_base64sha256":"CWefP74Q3uXBpYrILfuLXIxUm6crWg8vz3JVAfRsudU=","content_base64sha512":"a3fvqjCVWyy/5z/k1uh6XlYCdBHCX55wgVA72fvCwBJGP1+d+yWSOsEBpBpFLZQ0Sp2o68PCRDSYpVlaKbM1YA==","content_md5":"208e5ffcc53345c2a155127129c36795","content_sha1":"160173b6b9b8b49c9089fb8b8bb43bdda2d3de81","content_sha256":"09679f3fbe10dee5c1a58ac82dfb8b5c8c549ba72b5a0f2fcf725501f46cb9d5","content_sha512":"6b77efaa30955b2cbfe73fe4d6e87a5e56027411c25f9e7081503bd9fbc2c012463f5f9dfb25923ac101a41a452d94344a9da8ebc3c2443498a5595a29b33560","directory_permission":"0700","file_permission":"0700","filename":"out/clusterKey/id_ed25519_59699815-6d51-4e6f-b5b9-037781161bc4.zip","id":"160173b6b9b8b49c9089fb8b8bb43bdda2d3de81","source":null},"sensitive_attributes":[[{"type":"get_attr","value":"content"}],[{"type":"get_attr","value":"content_base64"}]],"dependencies":["module.ssh.autoglue_ssh_key.this","module.ssh.data.http.download","module.ssh.null_resource.mkdirs"]}]},{"module":"module.ssh[\"clusterKey\"]","mode":"managed","type":"null_resource","name":"mkdirs","provider":"provider[\"registry.opentofu.org/hashicorp/null\"]","instances":[{"index_key":0,"schema_version":0,"attributes":{"id":"4173767452342259878","triggers":null},"sensitive_attributes":[]}]}],"check_results":[{"object_kind":"resource","config_addr":"module.ssh.local_sensitive_file.zip","status":"pass","objects":[{"object_addr":"module.ssh[\"bastionKey\"].local_sensitive_file.zip[0]","status":"pass"},{"object_addr":"module.ssh[\"clusterKey\"].local_sensitive_file.zip[0]","status":"pass"}]},{"object_kind":"resource","config_addr":"module.servers.autoglue_server.this","status":"pass","objects":[{"object_addr":"module.servers.autoglue_server.this[\"bastion\"]","status":"pass"},{"object_addr":"module.servers.autoglue_server.this[\"manager1\"]","status":"pass"},{"object_addr":"module.servers.autoglue_server.this[\"agent1\"]","status":"pass"}]}]} diff --git a/terraform/envs/dev/terraform.tfvars b/terraform/envs/dev/terraform.tfvars index 27278bd..7c23a11 100644 --- a/terraform/envs/dev/terraform.tfvars +++ b/terraform/envs/dev/terraform.tfvars @@ -2,22 +2,22 @@ org_key = "org_lnJwmyyWH7JC-JgZo5v3Kw" org_secret = "fqd9yebGMfK6h5HSgWn4sXrwr9xlFbvbIYtNylRElMQ" ssh_keys = { - key1 = { - name = "CI deploy key 1" - comment = "deploy1@autoglue" + bastionKey = { + name = "Bastion Key" + comment = "deploy@autoglue" type = "rsa" bits = 4096 enable_download = true download_part = "both" - download_dir = "out/key1" + download_dir = "out/bastionKey" } - key2 = { - name = "CI deploy key 2" - comment = "deploy2@autoglue" + clusterKey = { + name = "Cluster Key" + comment = "bastion@autoglue" type = "ed25519" # bits ignored enable_download = true download_part = "both" - download_dir = "out/key2" + download_dir = "out/clusterKey" } } diff --git a/terraform/modules/servers/main.tf b/terraform/modules/servers/main.tf new file mode 100644 index 0000000..1a5f4be --- /dev/null +++ b/terraform/modules/servers/main.tf @@ -0,0 +1,39 @@ +locals { + # Resolve the SSH key ID for each server: + # Prefer explicit ssh_key_id, otherwise look up by ssh_key_ref in var.ssh_key_ids. + resolved_ssh_key_ids = { + for name, spec in var.servers : + name => coalesce( + try(spec.ssh_key_id, null), + try(var.ssh_key_ids[spec.ssh_key_ref], null) + ) + } +} + +resource "autoglue_server" "this" { + for_each = var.servers + + hostname = try(each.value.hostname, null) + private_ip_address = each.value.private_ip_address + public_ip_address = try(each.value.public_ip_address, null) + role = lower(each.value.role) + ssh_user = each.value.ssh_user + ssh_key_id = local.resolved_ssh_key_ids[each.key] + status = try(each.value.status, null) + + # Client-side guards to match your API rules + lifecycle { + precondition { + condition = local.resolved_ssh_key_ids[each.key] != null && local.resolved_ssh_key_ids[each.key] != "" + error_message = "Provide either ssh_key_id or ssh_key_ref (and pass ssh_key_ids to the module)." + } + precondition { + condition = lower(each.value.role) != "bastion" ? true : (try(each.value.public_ip_address, "") != "") + error_message = "public_ip_address is required when role == \"bastion\"." + } + precondition { + condition = try(each.value.status, "") == "" || contains(["pending", "provisioning", "ready", "failed"], lower(each.value.status)) + error_message = "status must be one of: pending, provisioning, ready, failed (or omitted)." + } + } +} diff --git a/terraform/modules/servers/outputs.tf b/terraform/modules/servers/outputs.tf new file mode 100644 index 0000000..25ed53b --- /dev/null +++ b/terraform/modules/servers/outputs.tf @@ -0,0 +1,28 @@ +output "ids" { + description = "Map of server IDs by key." + value = { for k, r in autoglue_server.this : k => r.id } +} + +output "statuses" { + description = "Map of server statuses by key." + value = { for k, r in autoglue_server.this : k => r.status } +} + +output "details" { + description = "Selected attributes for convenience." + value = { + for k, r in autoglue_server.this : k => { + id = r.id + organization_id = r.organization_id + hostname = r.hostname + private_ip_address = r.private_ip_address + public_ip_address = r.public_ip_address + role = r.role + ssh_user = r.ssh_user + ssh_key_id = r.ssh_key_id + status = r.status + created_at = r.created_at + updated_at = r.updated_at + } + } +} diff --git a/terraform/modules/servers/variables.tf b/terraform/modules/servers/variables.tf new file mode 100644 index 0000000..ec3296d --- /dev/null +++ b/terraform/modules/servers/variables.tf @@ -0,0 +1,34 @@ +variable "servers" { + description = <<-EOT + Map of servers to create. Example shape: + { + bastion = { + hostname = "bastion-01" + private_ip_address = "10.0.0.10" + public_ip_address = "54.12.34.56" # required when role = "bastion" + role = "bastion" + ssh_user = "ubuntu" + ssh_key_ref = "bastionKey" # OR set ssh_key_id instead + # ssh_key_id = "uuid-string" + # status = "pending|provisioning|ready|failed" + } + } + EOT + type = map(object({ + hostname = optional(string) + private_ip_address = string + public_ip_address = optional(string) + role = string + ssh_user = string + ssh_key_ref = optional(string) # name to look up in var.ssh_key_ids + ssh_key_id = optional(string) # direct UUID (overrides ssh_key_ref if set) + status = optional(string) # pending|provisioning|ready|failed + })) + default = {} +} + +variable "ssh_key_ids" { + description = "Map of SSH key IDs you can reference via servers[*].ssh_key_ref." + type = map(string) + default = {} +} diff --git a/terraform/modules/servers/versions.tf b/terraform/modules/servers/versions.tf new file mode 100644 index 0000000..60ae39d --- /dev/null +++ b/terraform/modules/servers/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5.0" + + required_providers { + autoglue = { + source = "glueops/autoglue/autoglue" + } + } +} \ No newline at end of file diff --git a/ui/src/sdk/.openapi-generator/FILES b/ui/src/sdk/.openapi-generator/FILES index 743d966..476ba7d 100644 --- a/ui/src/sdk/.openapi-generator/FILES +++ b/ui/src/sdk/.openapi-generator/FILES @@ -4,11 +4,13 @@ README.md docs/AuthApi.md docs/DtoAuthStartResponse.md +docs/DtoCreateLabelRequest.md docs/DtoCreateSSHRequest.md docs/DtoCreateServerRequest.md docs/DtoCreateTaintRequest.md docs/DtoJWK.md docs/DtoJWKS.md +docs/DtoLabelResponse.md docs/DtoLogoutRequest.md docs/DtoRefreshRequest.md docs/DtoServerResponse.md @@ -16,6 +18,7 @@ docs/DtoSshResponse.md docs/DtoSshRevealResponse.md docs/DtoTaintResponse.md docs/DtoTokenPair.md +docs/DtoUpdateLabelRequest.md docs/DtoUpdateServerRequest.md docs/DtoUpdateTaintRequest.md docs/HandlersCreateUserKeyRequest.md @@ -28,6 +31,7 @@ docs/HandlersOrgKeyCreateResp.md docs/HandlersOrgUpdateReq.md docs/HandlersUpdateMeRequest.md docs/HandlersUserAPIKeyOut.md +docs/LabelsApi.md docs/MeAPIKeysApi.md docs/MeApi.md docs/ModelsAPIKey.md @@ -41,6 +45,7 @@ docs/TaintsApi.md docs/UtilsErrorResponse.md package.json src/apis/AuthApi.ts +src/apis/LabelsApi.ts src/apis/MeAPIKeysApi.ts src/apis/MeApi.ts src/apis/OrgsApi.ts @@ -50,11 +55,13 @@ src/apis/TaintsApi.ts src/apis/index.ts src/index.ts src/models/DtoAuthStartResponse.ts +src/models/DtoCreateLabelRequest.ts src/models/DtoCreateSSHRequest.ts src/models/DtoCreateServerRequest.ts src/models/DtoCreateTaintRequest.ts src/models/DtoJWK.ts src/models/DtoJWKS.ts +src/models/DtoLabelResponse.ts src/models/DtoLogoutRequest.ts src/models/DtoRefreshRequest.ts src/models/DtoServerResponse.ts @@ -62,6 +69,7 @@ src/models/DtoSshResponse.ts src/models/DtoSshRevealResponse.ts src/models/DtoTaintResponse.ts src/models/DtoTokenPair.ts +src/models/DtoUpdateLabelRequest.ts src/models/DtoUpdateServerRequest.ts src/models/DtoUpdateTaintRequest.ts src/models/HandlersCreateUserKeyRequest.ts diff --git a/ui/src/sdk/apis/LabelsApi.ts b/ui/src/sdk/apis/LabelsApi.ts new file mode 100644 index 0000000..b623d5e --- /dev/null +++ b/ui/src/sdk/apis/LabelsApi.ts @@ -0,0 +1,359 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * AutoGlue API + * API for managing K3s clusters across cloud providers + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + DtoCreateLabelRequest, + DtoLabelResponse, + DtoUpdateLabelRequest, +} from '../models/index'; +import { + DtoCreateLabelRequestFromJSON, + DtoCreateLabelRequestToJSON, + DtoLabelResponseFromJSON, + DtoLabelResponseToJSON, + DtoUpdateLabelRequestFromJSON, + DtoUpdateLabelRequestToJSON, +} from '../models/index'; + +export interface CreateLabelRequest { + body: DtoCreateLabelRequest; + xOrgID?: string; +} + +export interface DeleteLabelRequest { + id: string; + xOrgID?: string; +} + +export interface GetLabelRequest { + id: string; + xOrgID?: string; +} + +export interface ListLabelsRequest { + xOrgID?: string; + key?: string; + value?: string; + q?: string; +} + +export interface UpdateLabelRequest { + id: string; + body: DtoUpdateLabelRequest; + xOrgID?: string; +} + +/** + * + */ +export class LabelsApi extends runtime.BaseAPI { + + /** + * Creates a label. + * Create label (org scoped) + */ + async createLabelRaw(requestParameters: CreateLabelRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['body'] == null) { + throw new runtime.RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling createLabel().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (requestParameters['xOrgID'] != null) { + headerParameters['X-Org-ID'] = String(requestParameters['xOrgID']); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + + let urlPath = `/labels`; + + const response = await this.request({ + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: DtoCreateLabelRequestToJSON(requestParameters['body']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => DtoLabelResponseFromJSON(jsonValue)); + } + + /** + * Creates a label. + * Create label (org scoped) + */ + async createLabel(requestParameters: CreateLabelRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.createLabelRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Permanently deletes the label. + * Delete label (org scoped) + */ + async deleteLabelRaw(requestParameters: DeleteLabelRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling deleteLabel().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (requestParameters['xOrgID'] != null) { + headerParameters['X-Org-ID'] = String(requestParameters['xOrgID']); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + + let urlPath = `/labels/{id}`; + urlPath = urlPath.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))); + + const response = await this.request({ + path: urlPath, + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + if (this.isJsonMime(response.headers.get('content-type'))) { + return new runtime.JSONApiResponse(response); + } else { + return new runtime.TextApiResponse(response) as any; + } + } + + /** + * Permanently deletes the label. + * Delete label (org scoped) + */ + async deleteLabel(requestParameters: DeleteLabelRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.deleteLabelRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Returns one label. + * Get label by ID (org scoped) + */ + async getLabelRaw(requestParameters: GetLabelRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling getLabel().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (requestParameters['xOrgID'] != null) { + headerParameters['X-Org-ID'] = String(requestParameters['xOrgID']); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + + let urlPath = `/labels/{id}`; + urlPath = urlPath.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))); + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => DtoLabelResponseFromJSON(jsonValue)); + } + + /** + * Returns one label. + * Get label by ID (org scoped) + */ + async getLabel(requestParameters: GetLabelRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getLabelRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups. + * List node labels (org scoped) + */ + async listLabelsRaw(requestParameters: ListLabelsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + const queryParameters: any = {}; + + if (requestParameters['key'] != null) { + queryParameters['key'] = requestParameters['key']; + } + + if (requestParameters['value'] != null) { + queryParameters['value'] = requestParameters['value']; + } + + if (requestParameters['q'] != null) { + queryParameters['q'] = requestParameters['q']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (requestParameters['xOrgID'] != null) { + headerParameters['X-Org-ID'] = String(requestParameters['xOrgID']); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + + let urlPath = `/labels`; + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(DtoLabelResponseFromJSON)); + } + + /** + * Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups. + * List node labels (org scoped) + */ + async listLabels(requestParameters: ListLabelsRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.listLabelsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Partially update label fields. + * Update label (org scoped) + */ + async updateLabelRaw(requestParameters: UpdateLabelRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling updateLabel().' + ); + } + + if (requestParameters['body'] == null) { + throw new runtime.RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling updateLabel().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (requestParameters['xOrgID'] != null) { + headerParameters['X-Org-ID'] = String(requestParameters['xOrgID']); + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-KEY"] = await this.configuration.apiKey("X-ORG-KEY"); // OrgKeyAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["X-ORG-SECRET"] = await this.configuration.apiKey("X-ORG-SECRET"); // OrgSecretAuth authentication + } + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // BearerAuth authentication + } + + + let urlPath = `/labels/{id}`; + urlPath = urlPath.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))); + + const response = await this.request({ + path: urlPath, + method: 'PATCH', + headers: headerParameters, + query: queryParameters, + body: DtoUpdateLabelRequestToJSON(requestParameters['body']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => DtoLabelResponseFromJSON(jsonValue)); + } + + /** + * Partially update label fields. + * Update label (org scoped) + */ + async updateLabel(requestParameters: UpdateLabelRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.updateLabelRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/ui/src/sdk/apis/index.ts b/ui/src/sdk/apis/index.ts index 810d92c..a2fe104 100644 --- a/ui/src/sdk/apis/index.ts +++ b/ui/src/sdk/apis/index.ts @@ -1,6 +1,7 @@ /* tslint:disable */ /* eslint-disable */ export * from './AuthApi'; +export * from './LabelsApi'; export * from './MeApi'; export * from './MeAPIKeysApi'; export * from './OrgsApi'; diff --git a/ui/src/sdk/docs/DtoCreateLabelRequest.md b/ui/src/sdk/docs/DtoCreateLabelRequest.md new file mode 100644 index 0000000..262c8ef --- /dev/null +++ b/ui/src/sdk/docs/DtoCreateLabelRequest.md @@ -0,0 +1,36 @@ + +# DtoCreateLabelRequest + + +## Properties + +Name | Type +------------ | ------------- +`key` | string +`value` | string + +## Example + +```typescript +import type { DtoCreateLabelRequest } from '@glueops/autoglue-sdk' + +// TODO: Update the object below with actual values +const example = { + "key": null, + "value": null, +} satisfies DtoCreateLabelRequest + +console.log(example) + +// Convert the instance to a JSON string +const exampleJSON: string = JSON.stringify(example) +console.log(exampleJSON) + +// Parse the JSON string back to an object +const exampleParsed = JSON.parse(exampleJSON) as DtoCreateLabelRequest +console.log(exampleParsed) +``` + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + + diff --git a/ui/src/sdk/docs/DtoLabelResponse.md b/ui/src/sdk/docs/DtoLabelResponse.md new file mode 100644 index 0000000..5931de0 --- /dev/null +++ b/ui/src/sdk/docs/DtoLabelResponse.md @@ -0,0 +1,38 @@ + +# DtoLabelResponse + + +## Properties + +Name | Type +------------ | ------------- +`id` | string +`key` | string +`value` | string + +## Example + +```typescript +import type { DtoLabelResponse } from '@glueops/autoglue-sdk' + +// TODO: Update the object below with actual values +const example = { + "id": null, + "key": null, + "value": null, +} satisfies DtoLabelResponse + +console.log(example) + +// Convert the instance to a JSON string +const exampleJSON: string = JSON.stringify(example) +console.log(exampleJSON) + +// Parse the JSON string back to an object +const exampleParsed = JSON.parse(exampleJSON) as DtoLabelResponse +console.log(exampleParsed) +``` + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + + diff --git a/ui/src/sdk/docs/DtoUpdateLabelRequest.md b/ui/src/sdk/docs/DtoUpdateLabelRequest.md new file mode 100644 index 0000000..139cb89 --- /dev/null +++ b/ui/src/sdk/docs/DtoUpdateLabelRequest.md @@ -0,0 +1,36 @@ + +# DtoUpdateLabelRequest + + +## Properties + +Name | Type +------------ | ------------- +`key` | string +`value` | string + +## Example + +```typescript +import type { DtoUpdateLabelRequest } from '@glueops/autoglue-sdk' + +// TODO: Update the object below with actual values +const example = { + "key": null, + "value": null, +} satisfies DtoUpdateLabelRequest + +console.log(example) + +// Convert the instance to a JSON string +const exampleJSON: string = JSON.stringify(example) +console.log(exampleJSON) + +// Parse the JSON string back to an object +const exampleParsed = JSON.parse(exampleJSON) as DtoUpdateLabelRequest +console.log(exampleParsed) +``` + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + + diff --git a/ui/src/sdk/docs/LabelsApi.md b/ui/src/sdk/docs/LabelsApi.md new file mode 100644 index 0000000..64c62ea --- /dev/null +++ b/ui/src/sdk/docs/LabelsApi.md @@ -0,0 +1,433 @@ +# LabelsApi + +All URIs are relative to *http://localhost:8080/api/v1* + +| Method | HTTP request | Description | +|------------- | ------------- | -------------| +| [**createLabel**](LabelsApi.md#createlabel) | **POST** /labels | Create label (org scoped) | +| [**deleteLabel**](LabelsApi.md#deletelabel) | **DELETE** /labels/{id} | Delete label (org scoped) | +| [**getLabel**](LabelsApi.md#getlabel) | **GET** /labels/{id} | Get label by ID (org scoped) | +| [**listLabels**](LabelsApi.md#listlabels) | **GET** /labels | List node labels (org scoped) | +| [**updateLabel**](LabelsApi.md#updatelabel) | **PATCH** /labels/{id} | Update label (org scoped) | + + + +## createLabel + +> DtoLabelResponse createLabel(body, xOrgID) + +Create label (org scoped) + +Creates a label. + +### Example + +```ts +import { + Configuration, + LabelsApi, +} from '@glueops/autoglue-sdk'; +import type { CreateLabelRequest } from '@glueops/autoglue-sdk'; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // DtoCreateLabelRequest | Label payload + body: ..., + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + } satisfies CreateLabelRequest; + + try { + const data = await api.createLabel(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + + +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **body** | [DtoCreateLabelRequest](DtoCreateLabelRequest.md) | Label payload | | +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | + +### Return type + +[**DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: `application/json` +- **Accept**: `application/json` + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **201** | Created | - | +| **400** | invalid json / missing fields / invalid node_pool_ids | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **500** | create failed | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + + +## deleteLabel + +> string deleteLabel(id, xOrgID) + +Delete label (org scoped) + +Permanently deletes the label. + +### Example + +```ts +import { + Configuration, + LabelsApi, +} from '@glueops/autoglue-sdk'; +import type { DeleteLabelRequest } from '@glueops/autoglue-sdk'; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // string | Label ID (UUID) + id: id_example, + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + } satisfies DeleteLabelRequest; + + try { + const data = await api.deleteLabel(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + + +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **id** | `string` | Label ID (UUID) | [Defaults to `undefined`] | +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | + +### Return type + +**string** + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `application/json` + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **204** | No Content | - | +| **400** | invalid id | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **500** | delete failed | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + + +## getLabel + +> DtoLabelResponse getLabel(id, xOrgID) + +Get label by ID (org scoped) + +Returns one label. + +### Example + +```ts +import { + Configuration, + LabelsApi, +} from '@glueops/autoglue-sdk'; +import type { GetLabelRequest } from '@glueops/autoglue-sdk'; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // string | Label ID (UUID) + id: id_example, + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + } satisfies GetLabelRequest; + + try { + const data = await api.getLabel(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + + +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **id** | `string` | Label ID (UUID) | [Defaults to `undefined`] | +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | + +### Return type + +[**DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `application/json` + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | OK | - | +| **400** | invalid id | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **404** | not found | - | +| **500** | fetch failed | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + + +## listLabels + +> Array<DtoLabelResponse> listLabels(xOrgID, key, value, q) + +List node labels (org scoped) + +Returns node labels for the organization in X-Org-ID. Filters: `key`, `value`, and `q` (key contains). Add `include=node_pools` to include linked node groups. + +### Example + +```ts +import { + Configuration, + LabelsApi, +} from '@glueops/autoglue-sdk'; +import type { ListLabelsRequest } from '@glueops/autoglue-sdk'; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + // string | Exact key (optional) + key: key_example, + // string | Exact value (optional) + value: value_example, + // string | Key contains (case-insensitive) (optional) + q: q_example, + } satisfies ListLabelsRequest; + + try { + const data = await api.listLabels(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + + +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | +| **key** | `string` | Exact key | [Optional] [Defaults to `undefined`] | +| **value** | `string` | Exact value | [Optional] [Defaults to `undefined`] | +| **q** | `string` | Key contains (case-insensitive) | [Optional] [Defaults to `undefined`] | + +### Return type + +[**Array<DtoLabelResponse>**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `application/json` + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | OK | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **500** | failed to list node taints | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + + +## updateLabel + +> DtoLabelResponse updateLabel(id, body, xOrgID) + +Update label (org scoped) + +Partially update label fields. + +### Example + +```ts +import { + Configuration, + LabelsApi, +} from '@glueops/autoglue-sdk'; +import type { UpdateLabelRequest } from '@glueops/autoglue-sdk'; + +async function example() { + console.log("🚀 Testing @glueops/autoglue-sdk SDK..."); + const config = new Configuration({ + // To configure API key authorization: OrgKeyAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: OrgSecretAuth + apiKey: "YOUR API KEY", + // To configure API key authorization: BearerAuth + apiKey: "YOUR API KEY", + }); + const api = new LabelsApi(config); + + const body = { + // string | Label ID (UUID) + id: id_example, + // DtoUpdateLabelRequest | Fields to update + body: ..., + // string | Organization UUID (optional) + xOrgID: xOrgID_example, + } satisfies UpdateLabelRequest; + + try { + const data = await api.updateLabel(body); + console.log(data); + } catch (error) { + console.error(error); + } +} + +// Run the test +example().catch(console.error); +``` + +### Parameters + + +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **id** | `string` | Label ID (UUID) | [Defaults to `undefined`] | +| **body** | [DtoUpdateLabelRequest](DtoUpdateLabelRequest.md) | Fields to update | | +| **xOrgID** | `string` | Organization UUID | [Optional] [Defaults to `undefined`] | + +### Return type + +[**DtoLabelResponse**](DtoLabelResponse.md) + +### Authorization + +[OrgKeyAuth](../README.md#OrgKeyAuth), [OrgSecretAuth](../README.md#OrgSecretAuth), [BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: `application/json` +- **Accept**: `application/json` + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | OK | - | +| **400** | invalid id / invalid json | - | +| **401** | Unauthorized | - | +| **403** | organization required | - | +| **404** | not found | - | +| **500** | update failed | - | + +[[Back to top]](#) [[Back to API list]](../README.md#api-endpoints) [[Back to Model list]](../README.md#models) [[Back to README]](../README.md) + diff --git a/ui/src/sdk/models/DtoCreateLabelRequest.ts b/ui/src/sdk/models/DtoCreateLabelRequest.ts new file mode 100644 index 0000000..19e9273 --- /dev/null +++ b/ui/src/sdk/models/DtoCreateLabelRequest.ts @@ -0,0 +1,73 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * AutoGlue API + * API for managing K3s clusters across cloud providers + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface DtoCreateLabelRequest + */ +export interface DtoCreateLabelRequest { + /** + * + * @type {string} + * @memberof DtoCreateLabelRequest + */ + key?: string; + /** + * + * @type {string} + * @memberof DtoCreateLabelRequest + */ + value?: string; +} + +/** + * Check if a given object implements the DtoCreateLabelRequest interface. + */ +export function instanceOfDtoCreateLabelRequest(value: object): value is DtoCreateLabelRequest { + return true; +} + +export function DtoCreateLabelRequestFromJSON(json: any): DtoCreateLabelRequest { + return DtoCreateLabelRequestFromJSONTyped(json, false); +} + +export function DtoCreateLabelRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): DtoCreateLabelRequest { + if (json == null) { + return json; + } + return { + + 'key': json['key'] == null ? undefined : json['key'], + 'value': json['value'] == null ? undefined : json['value'], + }; +} + +export function DtoCreateLabelRequestToJSON(json: any): DtoCreateLabelRequest { + return DtoCreateLabelRequestToJSONTyped(json, false); +} + +export function DtoCreateLabelRequestToJSONTyped(value?: DtoCreateLabelRequest | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'key': value['key'], + 'value': value['value'], + }; +} + diff --git a/ui/src/sdk/models/DtoLabelResponse.ts b/ui/src/sdk/models/DtoLabelResponse.ts new file mode 100644 index 0000000..3fb20fb --- /dev/null +++ b/ui/src/sdk/models/DtoLabelResponse.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * AutoGlue API + * API for managing K3s clusters across cloud providers + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface DtoLabelResponse + */ +export interface DtoLabelResponse { + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + id?: string; + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + key?: string; + /** + * + * @type {string} + * @memberof DtoLabelResponse + */ + value?: string; +} + +/** + * Check if a given object implements the DtoLabelResponse interface. + */ +export function instanceOfDtoLabelResponse(value: object): value is DtoLabelResponse { + return true; +} + +export function DtoLabelResponseFromJSON(json: any): DtoLabelResponse { + return DtoLabelResponseFromJSONTyped(json, false); +} + +export function DtoLabelResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): DtoLabelResponse { + if (json == null) { + return json; + } + return { + + 'id': json['id'] == null ? undefined : json['id'], + 'key': json['key'] == null ? undefined : json['key'], + 'value': json['value'] == null ? undefined : json['value'], + }; +} + +export function DtoLabelResponseToJSON(json: any): DtoLabelResponse { + return DtoLabelResponseToJSONTyped(json, false); +} + +export function DtoLabelResponseToJSONTyped(value?: DtoLabelResponse | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'key': value['key'], + 'value': value['value'], + }; +} + diff --git a/ui/src/sdk/models/DtoUpdateLabelRequest.ts b/ui/src/sdk/models/DtoUpdateLabelRequest.ts new file mode 100644 index 0000000..b73722c --- /dev/null +++ b/ui/src/sdk/models/DtoUpdateLabelRequest.ts @@ -0,0 +1,73 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * AutoGlue API + * API for managing K3s clusters across cloud providers + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface DtoUpdateLabelRequest + */ +export interface DtoUpdateLabelRequest { + /** + * + * @type {string} + * @memberof DtoUpdateLabelRequest + */ + key?: string; + /** + * + * @type {string} + * @memberof DtoUpdateLabelRequest + */ + value?: string; +} + +/** + * Check if a given object implements the DtoUpdateLabelRequest interface. + */ +export function instanceOfDtoUpdateLabelRequest(value: object): value is DtoUpdateLabelRequest { + return true; +} + +export function DtoUpdateLabelRequestFromJSON(json: any): DtoUpdateLabelRequest { + return DtoUpdateLabelRequestFromJSONTyped(json, false); +} + +export function DtoUpdateLabelRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): DtoUpdateLabelRequest { + if (json == null) { + return json; + } + return { + + 'key': json['key'] == null ? undefined : json['key'], + 'value': json['value'] == null ? undefined : json['value'], + }; +} + +export function DtoUpdateLabelRequestToJSON(json: any): DtoUpdateLabelRequest { + return DtoUpdateLabelRequestToJSONTyped(json, false); +} + +export function DtoUpdateLabelRequestToJSONTyped(value?: DtoUpdateLabelRequest | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'key': value['key'], + 'value': value['value'], + }; +} + diff --git a/ui/src/sdk/models/index.ts b/ui/src/sdk/models/index.ts index 8f2b763..4af595b 100644 --- a/ui/src/sdk/models/index.ts +++ b/ui/src/sdk/models/index.ts @@ -1,11 +1,13 @@ /* tslint:disable */ /* eslint-disable */ export * from './DtoAuthStartResponse'; +export * from './DtoCreateLabelRequest'; export * from './DtoCreateSSHRequest'; export * from './DtoCreateServerRequest'; export * from './DtoCreateTaintRequest'; export * from './DtoJWK'; export * from './DtoJWKS'; +export * from './DtoLabelResponse'; export * from './DtoLogoutRequest'; export * from './DtoRefreshRequest'; export * from './DtoServerResponse'; @@ -13,6 +15,7 @@ export * from './DtoSshResponse'; export * from './DtoSshRevealResponse'; export * from './DtoTaintResponse'; export * from './DtoTokenPair'; +export * from './DtoUpdateLabelRequest'; export * from './DtoUpdateServerRequest'; export * from './DtoUpdateTaintRequest'; export * from './HandlersCreateUserKeyRequest';