Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 52 additions & 14 deletions openapi/openapi.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
openapi: 3.0.0
info:
title: HyperFleet API
version: 1.0.0
version: 1.0.1
contact:
name: HyperFleet Team
license:
Expand Down Expand Up @@ -40,6 +40,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- BearerAuth: []
post:
operationId: postCluster
summary: Create cluster
Expand Down Expand Up @@ -71,6 +73,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ClusterCreateRequest'
security:
- BearerAuth: []
/api/hyperfleet/v1/clusters/{cluster_id}:
get:
operationId: getClusterById
Expand All @@ -97,6 +101,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- BearerAuth: []
/api/hyperfleet/v1/clusters/{cluster_id}/nodepools:
get:
operationId: getNodePoolsByClusterId
Expand Down Expand Up @@ -129,6 +135,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- BearerAuth: []
post:
operationId: createNodePool
summary: Create nodepool
Expand Down Expand Up @@ -161,6 +169,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/NodePoolCreateRequest'
security:
- BearerAuth: []
/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}:
get:
operationId: getNodePoolById
Expand Down Expand Up @@ -194,6 +204,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- BearerAuth: []
/api/hyperfleet/v1/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses:
post:
operationId: postNodePoolStatuses
Expand Down Expand Up @@ -307,6 +319,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AdapterStatusCreateRequest'
security:
- BearerAuth: []
get:
operationId: getClusterStatuses
summary: List all adapter statuses for cluster
Expand Down Expand Up @@ -334,6 +348,8 @@ paths:
description: The server could not understand the request due to invalid syntax.
'404':
description: The server cannot find the requested resource.
security:
- BearerAuth: []
/api/hyperfleet/v1/nodepools:
get:
operationId: getNodePools
Expand All @@ -360,12 +376,9 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- BearerAuth: []
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer

parameters:
QueryParams.order:
name: order
Expand Down Expand Up @@ -404,13 +417,12 @@ components:
name: search
in: query
required: false
description: |-
Filter results using TSL (Tree Search Language) query syntax.
Examples: `status.phase='NotReady'`, `name in ('c1','c2')`, `labels.region='us-east'`
schema:
type: string
explode: false
description: |
Filter results using TSL (Tree Search Language) query syntax.

Examples: `status.phase='NotReady'`, `name in ('c1','c2')`, `labels.region='us-east'`
schemas:
APIResource:
type: object
Expand Down Expand Up @@ -913,6 +925,7 @@ components:
- updated_time
- created_by
- updated_by
- generation
- owner_references
- status
properties:
Expand All @@ -928,6 +941,11 @@ components:
updated_by:
type: string
format: email
generation:
type: integer
format: int32
minimum: 1
description: Generation field is updated on customer updates, reflecting the version of the "intent" of the customer
owner_references:
$ref: '#/components/schemas/ObjectReference'
status:
Expand All @@ -943,6 +961,7 @@ components:
environment: production
pooltype: worker
spec: {}
generation: 1
created_time: '2021-01-01T00:00:00Z'
updated_time: '2021-01-01T00:00:00Z'
created_by: user-123@example.com
Expand Down Expand Up @@ -995,12 +1014,15 @@ components:
description: Resource URI
name:
type: string
minLength: 3
maxLength: 63
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
description: NodePool name (unique in a cluster)
spec:
allOf:
- $ref: '#/components/schemas/NodePoolSpec'
description: |-
Cluster specification
NodePool specification
CLM doesn't know how to unmarshall the spec, it only stores and forwards to adapters to do their job
But CLM will validate the schema before accepting the request
NodePoolCreateRequest:
Expand All @@ -1025,12 +1047,15 @@ components:
description: Resource URI
name:
type: string
minLength: 3
maxLength: 63
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
description: NodePool name (unique in a cluster)
spec:
allOf:
- $ref: '#/components/schemas/NodePoolSpec'
description: |-
Cluster specification
NodePool specification
CLM doesn't know how to unmarshall the spec, it only stores and forwards to adapters to do their job
But CLM will validate the schema before accepting the request
example:
Expand All @@ -1046,6 +1071,7 @@ components:
- updated_time
- created_by
- updated_by
- generation
- owner_references
- status
- name
Expand All @@ -1063,6 +1089,11 @@ components:
updated_by:
type: string
format: email
generation:
type: integer
format: int32
minimum: 1
description: Generation field is updated on customer updates, reflecting the version of the "intent" of the customer
owner_references:
$ref: '#/components/schemas/ObjectReference'
status:
Expand All @@ -1083,12 +1114,15 @@ components:
description: Resource URI
name:
type: string
minLength: 3
maxLength: 63
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
description: NodePool name (unique in a cluster)
spec:
allOf:
- $ref: '#/components/schemas/NodePoolSpec'
description: |-
Cluster specification
NodePool specification
CLM doesn't know how to unmarshall the spec, it only stores and forwards to adapters to do their job
But CLM will validate the schema before accepting the request
NodePoolList:
Expand Down Expand Up @@ -1205,7 +1239,11 @@ components:
- Ready
- Failed
description: Phase of a resource (Cluster or NodePool)
securitySchemes:
BearerAuth:
type: http
scheme: bearer
servers:
- url: https://api.hyperfleet.redhat.com
- url: https://hyperfleet.redhat.com
description: Production
variables: {}
6 changes: 2 additions & 4 deletions pkg/api/cluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ func (c *Cluster) BeforeUpdate(tx *gorm.DB) error {
}

type ClusterPatchRequest struct {
Name *string `json:"name,omitempty"`
Spec *map[string]interface{} `json:"spec,omitempty"`
Generation *int32 `json:"generation,omitempty"`
Labels *map[string]string `json:"labels,omitempty"`
Spec *map[string]interface{} `json:"spec,omitempty"`
Labels *map[string]string `json:"labels,omitempty"`
}
7 changes: 6 additions & 1 deletion pkg/api/node_pool_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type NodePool struct {
Labels datatypes.JSON `json:"labels,omitempty" gorm:"type:jsonb"`
Href string `json:"href,omitempty" gorm:"size:500"`

// Version control
Generation int32 `json:"generation" gorm:"default:1;not null"`

// Owner references (expanded)
OwnerID string `json:"owner_id" gorm:"size:255;not null;index"`
OwnerKind string `json:"owner_kind" gorm:"size:50;not null"`
Expand Down Expand Up @@ -55,6 +58,9 @@ func (np *NodePool) BeforeCreate(tx *gorm.DB) error {
np.ID = NewID()
np.CreatedTime = now
np.UpdatedTime = now
if np.Generation == 0 {
np.Generation = 1
}
if np.OwnerKind == "" {
np.OwnerKind = "Cluster"
}
Expand All @@ -78,7 +84,6 @@ func (np *NodePool) BeforeUpdate(tx *gorm.DB) error {
}

type NodePoolPatchRequest struct {
Name *string `json:"name,omitempty"`
Spec *map[string]interface{} `json:"spec,omitempty"`
Labels *map[string]string `json:"labels,omitempty"`
}
13 changes: 7 additions & 6 deletions pkg/api/presenters/node_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,13 @@ func PresentNodePool(nodePool *api.NodePool) openapi.NodePool {

kind := nodePool.Kind
result := openapi.NodePool{
Id: &nodePool.ID,
Kind: &kind,
Href: &href,
Name: nodePool.Name,
Spec: spec,
Labels: &labels,
Id: &nodePool.ID,
Kind: &kind,
Href: &href,
Name: nodePool.Name,
Spec: spec,
Labels: &labels,
Generation: nodePool.Generation,
OwnerReferences: openapi.ObjectReference{
Id: &nodePool.OwnerID,
Kind: &nodePool.OwnerKind,
Expand Down
18 changes: 18 additions & 0 deletions pkg/dao/node_pool.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dao

import (
"bytes"
"context"

"gorm.io/gorm/clause"
Expand Down Expand Up @@ -48,6 +49,23 @@ func (d *sqlNodePoolDao) Create(ctx context.Context, nodePool *api.NodePool) (*a

func (d *sqlNodePoolDao) Replace(ctx context.Context, nodePool *api.NodePool) (*api.NodePool, error) {
g2 := (*d.sessionFactory).New(ctx)

// Get the existing nodePool to compare spec
existing, err := d.Get(ctx, nodePool.ID)
if err != nil {
db.MarkForRollback(ctx, err)
return nil, err
}

// Compare spec: if changed, increment generation
if !bytes.Equal(existing.Spec, nodePool.Spec) {
nodePool.Generation = existing.Generation + 1
} else {
// Spec unchanged, preserve generation
nodePool.Generation = existing.Generation
}

// Save the nodePool
if err := g2.Omit(clause.Associations).Save(nodePool).Error; err != nil {
db.MarkForRollback(ctx, err)
return nil, err
Expand Down
7 changes: 0 additions & 7 deletions pkg/handlers/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,13 @@ func (h clusterHandler) Patch(w http.ResponseWriter, r *http.Request) {
return nil, err
}

//patch a field
if patch.Name != nil {
found.Name = *patch.Name
}
if patch.Spec != nil {
specJSON, err := json.Marshal(*patch.Spec)
if err != nil {
return nil, errors.GeneralError("Failed to marshal spec: %v", err)
}
found.Spec = specJSON
}
if patch.Generation != nil {
found.Generation = *patch.Generation
}

clusterModel, err := h.cluster.Replace(ctx, found)
if err != nil {
Expand Down
4 changes: 0 additions & 4 deletions pkg/handlers/node_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ func (h nodePoolHandler) Patch(w http.ResponseWriter, r *http.Request) {
return nil, err
}

//patch a field
if patch.Name != nil {
found.Name = *patch.Name
}
if patch.Spec != nil {
specJSON, err := json.Marshal(*patch.Spec)
if err != nil {
Expand Down
3 changes: 1 addition & 2 deletions pkg/services/node_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,7 @@ func (s *sqlNodePoolService) UpdateNodePoolStatusFromAdapters(ctx context.Contex
}

// Compute overall phase using required adapters
// NodePool doesn't have a generation field, so we pass 0 which means "don't check generation"
newPhase := ComputePhase(ctx, adapterStatuses, requiredNodePoolAdapters, 0)
newPhase := ComputePhase(ctx, adapterStatuses, requiredNodePoolAdapters, nodePool.Generation)

// Calculate min(adapters[].last_report_time) for nodepool.status.last_updated_time
// This uses the OLDEST adapter timestamp to ensure Sentinel can detect stale adapters
Expand Down