Skip to content

Commit 384955c

Browse files
authored
Merge pull request #29 from platform9/feat/cluster-blueprint
feat(resmgr): cluster-blueprint family — REST client + host_config/host_role/host_config_assignment + blueprint DS
2 parents b985be5 + 3ba4c95 commit 384955c

19 files changed

Lines changed: 1018 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ All notable changes to this project are documented here. The format is based on
9090
provider). Imported by a composite `<project_id>/<region>` ID (legacy bare `<project_id>` is
9191
also accepted). Cinder per-volume-type quotas (`volume_type_quota`) are not yet implemented.
9292

93+
- Cluster blueprint / host management (PCD `resmgr` API — the first non-OpenStack, non-gophercloud
94+
service): a thin `resmgr` v2 REST client (`clients.Config.ResmgrV2Client()`, endpoint resolved
95+
from the Keystone catalog, token via the shared ProviderClient) plus `pcd_host_config` (interface
96+
↔ traffic-type mapping and physical-network labels), `pcd_host_role` (assign a role such as
97+
`pf9-ostackhost-neutron` to a host), `pcd_host_config_assignment` (attach a host config to a host),
98+
and a `pcd_cluster_blueprint` data source (read a blueprint by name). The `pcd_cluster_blueprint`
99+
resource (write path) is tracked separately — see DECISIONS.md.
93100
- Registry documentation generation wired via `tfplugindocs` (`make generate`) — renders
94101
`docs/` for every resource and data source plus the provider index from schema
95102
descriptions. Generated docs are produced on demand / at release and are not committed.

DECISIONS.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,28 @@ Both PENDING items are lab-side configuration gaps (Platform9 / lab-ops), not pr
103103
defects; their acceptance tests flip green on a properly-configured PCD cloud.
104104

105105

106+
## 2026-07-13 — api-docs coverage: PCD-native services (resmgr / cluster blueprint)
107+
108+
An audit of `docs.platform9.com/api-docs` (7 services) vs the provider found the
109+
OpenStack-service families fully/near-covered, but two PCD-native services entirely
110+
unbuilt: **cluster-blueprint** (`resmgr/v2`) and **kubernetes** (PCD-K/CAPI). Tier-2
111+
gaps (blockstorage volume_type/snapshot/backup, identity group) were closed first
112+
(#26, #27).
113+
114+
For the cluster-blueprint family, `resmgr` is the first **non-OpenStack** service, so it
115+
gets a hand-written REST client (`Config.ResmgrV2Client()`) that resolves the `resmgr`
116+
catalog endpoint and reuses the authenticated ProviderClient for tokens (verified live
117+
against the CE lab). Shipped: `pcd_host_config`, `pcd_host_role`,
118+
`pcd_host_config_assignment`, and a `pcd_cluster_blueprint` **data source**.
119+
120+
**The `pcd_cluster_blueprint` resource (write path) is deliberately deferred to a focused
121+
follow-up.** Its object is uniquely hairy: a full-object `PUT` (partial models risk
122+
clearing fields), a `storageBackends` map that carries **plaintext driver credentials**,
123+
and create semantics (`POST` vs `PUT`) that cannot be safely verified without mutating the
124+
lab's single working blueprint. It deserves its own careful pass. The mutating resmgr
125+
resources' acceptance tests are opt-in (`PCD_ACC_RESMGR`) so they never touch a live
126+
cluster by accident.
127+
106128
## 2026-07-12 — backed out code PCD does not ship (VPNaaS, Octavia L7)
107129

108130
Live inspection of the CE lab's service catalog (all 14 services + the Octavia provider
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
data "pcd_cluster_blueprint" "example" {
2+
name = "ce-cluster"
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
terraform import pcd_host_config.example <host_config_id>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
resource "pcd_host_config" "example" {
2+
name = "hc-single-nic"
3+
mgmt_interface = "enp1s0"
4+
5+
network_labels = {
6+
physnet1 = "enp1s0"
7+
}
8+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
terraform import pcd_host_config_assignment.example <host_id>/<host_config_id>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
resource "pcd_host_config" "example" {
2+
name = "hc-single-nic"
3+
mgmt_interface = "enp1s0"
4+
network_labels = { physnet1 = "enp1s0" }
5+
}
6+
7+
resource "pcd_host_config_assignment" "example" {
8+
host_id = "04575315-80ce-4617-9b96-6611d00c9942"
9+
host_config_id = pcd_host_config.example.id
10+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
terraform import pcd_host_role.example <host_id>/<role_name>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# host_id is the resmgr host UUID of an onboarded host.
2+
resource "pcd_host_role" "example" {
3+
host_id = "04575315-80ce-4617-9b96-6611d00c9942"
4+
role_name = "pf9-ostackhost-neutron"
5+
}

internal/clients/config.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,30 @@ func (c *Config) BlockStorageV3Client() (*gophercloud.ServiceClient, error) {
235235
return client, nil
236236
}
237237

238+
// ResmgrV2Client returns a client for the PCD resource manager (`resmgr`) v2
239+
// API. resmgr is a Platform9-specific REST service (not part of OpenStack, so it
240+
// has no gophercloud constructor); it backs cluster blueprints, host configs,
241+
// and host role/config assignment. The endpoint is resolved from the Keystone
242+
// catalog (service type `resmgr`) and the shared authenticated ProviderClient
243+
// supplies the token, so requests use `client.Get/Post/Put/Delete`.
244+
func (c *Config) ResmgrV2Client() (*gophercloud.ServiceClient, error) {
245+
url, err := c.Provider.EndpointLocator(gophercloud.EndpointOpts{
246+
Type: "resmgr",
247+
Region: c.Region,
248+
Availability: gophercloud.AvailabilityPublic,
249+
})
250+
if err != nil {
251+
return nil, fmt.Errorf("pcd: locating resmgr endpoint: %w", err)
252+
}
253+
client := &gophercloud.ServiceClient{
254+
ProviderClient: c.Provider,
255+
Endpoint: strings.TrimRight(url, "/") + "/v2/",
256+
Type: "resmgr",
257+
}
258+
c.applyOverride(client, "resmgr")
259+
return client, nil
260+
}
261+
238262
// LoadBalancerV2Client returns an Octavia v2 service client, honoring an
239263
// endpoint_overrides entry for the "load-balancer" service type if present.
240264
func (c *Config) LoadBalancerV2Client() (*gophercloud.ServiceClient, error) {

0 commit comments

Comments
 (0)