This is an automated email from the ASF dual-hosted git repository.

DImuthuUpe pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 13ef537fd8b5a7438fc654f4ddd2084aac944b70
Author: DImuthuUpe <[email protected]>
AuthorDate: Tue May 19 21:12:58 2026 -0400

    Updating account resource limits when the allocation resource mapping is 
created
---
 .../internal/subscribers/account.go                | 60 ++++++++++++++++++++++
 .../internal/subscribers/members.go                |  4 +-
 .../internal/subscribers/subscriber.go             |  1 +
 docs/API-Docs.md                                   | 56 +++++++++++++++++---
 ...te_allocation_resource_mapping_amounts.down.sql | 20 ++++++++
 ...pute_allocation_resource_mapping_amounts.up.sql | 20 ++++++++
 internal/server/server.go                          | 24 ++++++++-
 .../compute_allocation_resource_mapping_store.go   | 33 ++++++++++--
 internal/store/store.go                            |  4 ++
 pkg/models/allocation.go                           | 10 ++--
 pkg/service/compute_allocation_resource_mapping.go | 50 ++++++++++++++++--
 11 files changed, 262 insertions(+), 20 deletions(-)

diff --git 
a/connectors/SLURM/Association-Mapper/internal/subscribers/account.go 
b/connectors/SLURM/Association-Mapper/internal/subscribers/account.go
index 2b872d54c..70a0f49e6 100644
--- a/connectors/SLURM/Association-Mapper/internal/subscribers/account.go
+++ b/connectors/SLURM/Association-Mapper/internal/subscribers/account.go
@@ -59,3 +59,63 @@ func (a *AssociationSubscriber) 
SubscribeToComputeAllocationDeletion(computeAcco
 func (a *AssociationSubscriber) 
SubscribeToComputeAllocationUpdate(computeAccount models.ComputeAllocation) {
        slog.Info("Received compute allocation update event", "account", 
computeAccount)
 }
+
+func (a *AssociationSubscriber) 
SubscribeToComputeAllocationResourceMappingCreation(mapping 
models.ComputeAllocationResourceMapping) {
+       slog.Info("Received compute allocation resource mapping creation 
event", "mapping", mapping)
+
+       ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+       defer cancel()
+
+       allocation, err := a.coreService.GetComputeAllocation(ctx, 
mapping.ComputeAllocationID)
+       if err != nil {
+               slog.Error("Failed to get compute allocation for resource 
mapping creation", "error", err)
+               return
+       }
+
+       cluster, err := a.coreService.GetComputeCluster(ctx, 
allocation.ComputeClusterID)
+       if err != nil {
+               slog.Error("Failed to get compute cluster for resource mapping 
creation", "error", err)
+               return
+       }
+
+       resource, err := a.coreService.GetComputeAllocationResource(ctx, 
mapping.ComputeAllocationResourceID)
+       if err != nil {
+               slog.Error("Failed to get compute allocation resource for 
resource mapping creation", "error", err)
+               return
+       }
+
+       grpTres := []client.TRES{}
+
+       if mapping.ResourceAmount > 0 {
+               grpTres = append(grpTres, client.TRES{
+                       Type:  resource.ResourceType,
+                       Count: mapping.ResourceAmount,
+               })
+       }
+
+       grpTresMins := []client.TRES{}
+       if mapping.ResourceTime > 0 {
+               grpTresMins = append(grpTresMins, client.TRES{
+                       Type:  resource.ResourceType,
+                       Count: mapping.ResourceTime,
+               })
+       }
+
+       limits := client.AssocLimits{
+               GrpTRES:     grpTres,
+               GrpTRESMins: grpTresMins,
+       }
+
+       association := client.Association{
+               Account: allocation.Name,
+               Cluster: cluster.Name,
+               Limits:  limits,
+       }
+
+       err = a.slurmClient.UpsertAssociation(association)
+       if err != nil {
+               slog.Error("Failed to upsert association for membership 
resource override creation", "error", err)
+       } else {
+               slog.Info("Successfully upserted association for membership 
resource override creation", "association", association)
+       }
+}
diff --git 
a/connectors/SLURM/Association-Mapper/internal/subscribers/members.go 
b/connectors/SLURM/Association-Mapper/internal/subscribers/members.go
index 687ecd8d5..dfcc4ee70 100644
--- a/connectors/SLURM/Association-Mapper/internal/subscribers/members.go
+++ b/connectors/SLURM/Association-Mapper/internal/subscribers/members.go
@@ -118,7 +118,7 @@ func (a *AssociationSubscriber) 
SubscribeToComputeAllocationMembershipResourceOv
 
        if allocationResource.ResourceType == "GrpTRES" {
                grpTres = append(grpTres, client.TRES{
-                       Type:  allocationResource.Name,
+                       Type:  allocationResource.ResourceType,
                        Count: override.OverriddenResourceAmount, // 
override.OverriddenResourceAmount is the SU amount, but SLURM needs the actual 
resource amount (e.g., number of CPU hours), so we need to convert it using the 
rate for the resource
                })
        }
@@ -127,7 +127,7 @@ func (a *AssociationSubscriber) 
SubscribeToComputeAllocationMembershipResourceOv
 
        if allocationResource.ResourceType == "GrpTRESMins" {
                grpTresMins = append(grpTresMins, client.TRES{
-                       Type:  allocationResource.Name,
+                       Type:  allocationResource.ResourceType,
                        Count: override.OverriddenResourceAmount,
                })
        }
diff --git 
a/connectors/SLURM/Association-Mapper/internal/subscribers/subscriber.go 
b/connectors/SLURM/Association-Mapper/internal/subscribers/subscriber.go
index 17b65cce8..308928a4b 100644
--- a/connectors/SLURM/Association-Mapper/internal/subscribers/subscriber.go
+++ b/connectors/SLURM/Association-Mapper/internal/subscribers/subscriber.go
@@ -24,4 +24,5 @@ func (a *AssociationSubscriber) RegisterSubscribers() {
        
a.eventBus.SubscribeComputeAllocationUpdated(a.SubscribeToComputeAllocationUpdate)
        
a.eventBus.SubscribeComputeAllocationMembershipCreated(a.SubscribeToComputeAllocationMembershipCreation)
        
a.eventBus.SubscribeComputeAllocationMembershipResourceOverrideCreated(a.SubscribeToComputeAllocationMembershipResourceOverrideCreation)
+       
a.eventBus.SubscribeComputeAllocationResourceMappingCreated(a.SubscribeToComputeAllocationResourceMappingCreation)
 }
diff --git a/docs/API-Docs.md b/docs/API-Docs.md
index b571c8255..325a219bc 100644
--- a/docs/API-Docs.md
+++ b/docs/API-Docs.md
@@ -619,15 +619,21 @@ removed.
 
 ### `POST /compute-allocations/{id}/resources`
 
-Attach an existing resource to a compute allocation.
+Attach an existing resource to a compute allocation, recording the amount of
+the resource and the wall-clock time granted to the allocation.
 
 **Path parameters:** `{id}` — the compute allocation ID.
 **Required body fields:** `compute_allocation_resource_id`
+**Optional body fields:** `resource_amount` (int64, default `0`), 
`resource_time` (int64, default `0`). Both must be non-negative.
 
 **Request**
 
 ```json
-{ "compute_allocation_resource_id": "c0a1b2c3-d4e5-46f7-8899-aabbccddeeff" }
+{
+  "compute_allocation_resource_id": "c0a1b2c3-d4e5-46f7-8899-aabbccddeeff",
+  "resource_amount": 24,
+  "resource_time": 1440
+}
 ```
 
 **Response 201**
@@ -636,17 +642,55 @@ Attach an existing resource to a compute allocation.
 {
   "id": "7e1d2c3b-4a5f-4b6c-9d8e-0011223344ff",
   "compute_allocation_id": "2f6a8c1d-3e4b-4a7d-8c91-aa12bb34cc56",
-  "compute_allocation_resource_id": "c0a1b2c3-d4e5-46f7-8899-aabbccddeeff"
+  "compute_allocation_resource_id": "c0a1b2c3-d4e5-46f7-8899-aabbccddeeff",
+  "resource_amount": 24,
+  "resource_time": 1440
 }
 ```
 
 **Errors**
 
-- `400` — `compute_allocation_resource_id` missing, or either the allocation 
or the resource does not exist.
+- `400` — `compute_allocation_resource_id` missing, `resource_amount` / 
`resource_time` negative, or either the allocation or the resource does not 
exist.
 - `409` — this resource is already attached to the allocation.
 
 ---
 
+### `PUT /compute-allocations/{id}/resources/{resourceId}`
+
+Update the `resource_amount` and `resource_time` recorded on an existing
+(allocation, resource) mapping.
+
+**Path parameters:** `{id}` — the compute allocation ID; `{resourceId}` — the 
compute allocation resource ID.
+**Required body fields:** `resource_amount`, `resource_time` (both 
non-negative int64).
+
+**Request**
+
+```json
+{
+  "resource_amount": 48,
+  "resource_time": 2880
+}
+```
+
+**Response 200**
+
+```json
+{
+  "id": "7e1d2c3b-4a5f-4b6c-9d8e-0011223344ff",
+  "compute_allocation_id": "2f6a8c1d-3e4b-4a7d-8c91-aa12bb34cc56",
+  "compute_allocation_resource_id": "c0a1b2c3-d4e5-46f7-8899-aabbccddeeff",
+  "resource_amount": 48,
+  "resource_time": 2880
+}
+```
+
+**Errors**
+
+- `400` — either id missing, or `resource_amount` / `resource_time` negative.
+- `404` — no such mapping exists.
+
+---
+
 ### `DELETE /compute-allocations/{id}/resources/{resourceId}`
 
 Detach a resource from a compute allocation.
@@ -1276,7 +1320,7 @@ RES_ID=$(curl -s -X POST 
$BASE/compute-allocation-resources \
 # Attach the resource to the allocation.
 curl -s -X POST $BASE/compute-allocations/$ALLOC_ID/resources \
   -H 'Content-Type: application/json' \
-  -d "{\"compute_allocation_resource_id\":\"$RES_ID\"}" | jq
+  -d 
"{\"compute_allocation_resource_id\":\"$RES_ID\",\"resource_amount\":24,\"resource_time\":1440}"
 | jq
 
 # Define a rate for the resource.
 curl -s -X POST $BASE/compute-allocation-resource-rates \
@@ -1329,7 +1373,7 @@ OVERRIDE_ID=$(curl -s -X POST 
$BASE/compute-allocation-membership-resource-overr
   -d "{
         \"compute_allocation_membership_id\":\"$MEMBERSHIP_ID\",
         \"compute_allocation_resource_id\":\"$RES_ID\",
-        \"overridden_resource_amount\":10000
+        \"overridden_resource_amount\":10
       }" | jq -r .id)
 
 # Bump the override amount.
diff --git 
a/internal/db/migrations/000012_compute_allocation_resource_mapping_amounts.down.sql
 
b/internal/db/migrations/000012_compute_allocation_resource_mapping_amounts.down.sql
new file mode 100644
index 000000000..b792b9027
--- /dev/null
+++ 
b/internal/db/migrations/000012_compute_allocation_resource_mapping_amounts.down.sql
@@ -0,0 +1,20 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements.  See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership.  The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License.  You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied.  See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+ALTER TABLE compute_allocation_resource_mappings
+    DROP COLUMN resource_time,
+    DROP COLUMN resource_amount;
diff --git 
a/internal/db/migrations/000012_compute_allocation_resource_mapping_amounts.up.sql
 
b/internal/db/migrations/000012_compute_allocation_resource_mapping_amounts.up.sql
new file mode 100644
index 000000000..120491635
--- /dev/null
+++ 
b/internal/db/migrations/000012_compute_allocation_resource_mapping_amounts.up.sql
@@ -0,0 +1,20 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements.  See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership.  The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License.  You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied.  See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+ALTER TABLE compute_allocation_resource_mappings
+    ADD COLUMN resource_amount BIGINT NOT NULL DEFAULT 0 AFTER 
compute_allocation_resource_id,
+    ADD COLUMN resource_time   BIGINT NOT NULL DEFAULT 0 AFTER resource_amount;
diff --git a/internal/server/server.go b/internal/server/server.go
index 960799fd8..6af71d2df 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -81,6 +81,7 @@ func (s *Server) routes() {
 
        s.mux.HandleFunc("GET /compute-allocations/{id}/resources", 
s.listResourcesForAllocation)
        s.mux.HandleFunc("POST /compute-allocations/{id}/resources", 
s.attachResourceToAllocation)
+       s.mux.HandleFunc("PUT 
/compute-allocations/{id}/resources/{resourceId}", 
s.updateAllocationResourceMapping)
        s.mux.HandleFunc("DELETE 
/compute-allocations/{id}/resources/{resourceId}", 
s.detachResourceFromAllocation)
        s.mux.HandleFunc("GET /compute-allocation-resources/{id}/allocations", 
s.listAllocationsForResource)
 
@@ -366,6 +367,8 @@ func (s *Server) listComputeAllocationResources(w 
http.ResponseWriter, r *http.R
 
 type attachResourceRequest struct {
        ComputeAllocationResourceID string 
`json:"compute_allocation_resource_id"`
+       ResourceAmount              int64  `json:"resource_amount"`
+       ResourceTime                int64  `json:"resource_time"`
 }
 
 func (s *Server) attachResourceToAllocation(w http.ResponseWriter, r 
*http.Request) {
@@ -374,7 +377,7 @@ func (s *Server) attachResourceToAllocation(w 
http.ResponseWriter, r *http.Reque
                writeError(w, http.StatusBadRequest, err)
                return
        }
-       mapping, err := s.svc.AttachResourceToAllocation(r.Context(), 
r.PathValue("id"), body.ComputeAllocationResourceID)
+       mapping, err := s.svc.AttachResourceToAllocation(r.Context(), 
r.PathValue("id"), body.ComputeAllocationResourceID, body.ResourceAmount, 
body.ResourceTime)
        if err != nil {
                writeServiceError(w, err)
                return
@@ -382,6 +385,25 @@ func (s *Server) attachResourceToAllocation(w 
http.ResponseWriter, r *http.Reque
        writeJSON(w, http.StatusCreated, mapping)
 }
 
+type updateAllocationResourceMappingRequest struct {
+       ResourceAmount int64 `json:"resource_amount"`
+       ResourceTime   int64 `json:"resource_time"`
+}
+
+func (s *Server) updateAllocationResourceMapping(w http.ResponseWriter, r 
*http.Request) {
+       var body updateAllocationResourceMappingRequest
+       if err := decodeJSON(r, &body); err != nil {
+               writeError(w, http.StatusBadRequest, err)
+               return
+       }
+       mapping, err := s.svc.UpdateAllocationResourceMapping(r.Context(), 
r.PathValue("id"), r.PathValue("resourceId"), body.ResourceAmount, 
body.ResourceTime)
+       if err != nil {
+               writeServiceError(w, err)
+               return
+       }
+       writeJSON(w, http.StatusOK, mapping)
+}
+
 func (s *Server) detachResourceFromAllocation(w http.ResponseWriter, r 
*http.Request) {
        if err := s.svc.DetachResourceFromAllocation(r.Context(), 
r.PathValue("id"), r.PathValue("resourceId")); err != nil {
                writeServiceError(w, err)
diff --git a/internal/store/compute_allocation_resource_mapping_store.go 
b/internal/store/compute_allocation_resource_mapping_store.go
index ca89bbf06..9f05bf70b 100644
--- a/internal/store/compute_allocation_resource_mapping_store.go
+++ b/internal/store/compute_allocation_resource_mapping_store.go
@@ -37,10 +37,25 @@ func NewComputeAllocationResourceMappingStore(db *sqlx.DB) 
ComputeAllocationReso
        return &mysqlComputeAllocationResourceMappingStore{db: db}
 }
 
+func (s *mysqlComputeAllocationResourceMappingStore) FindByID(ctx 
context.Context, id string) (*models.ComputeAllocationResourceMapping, error) {
+       var m models.ComputeAllocationResourceMapping
+       err := s.db.GetContext(ctx, &m,
+               `SELECT id, compute_allocation_id, 
compute_allocation_resource_id, resource_amount, resource_time
+                FROM compute_allocation_resource_mappings
+                WHERE id = ?`, id)
+       if err != nil {
+               if errors.Is(err, sql.ErrNoRows) {
+                       return nil, nil
+               }
+               return nil, err
+       }
+       return &m, nil
+}
+
 func (s *mysqlComputeAllocationResourceMappingStore) FindByPair(ctx 
context.Context, allocationID, resourceID string) 
(*models.ComputeAllocationResourceMapping, error) {
        var m models.ComputeAllocationResourceMapping
        err := s.db.GetContext(ctx, &m,
-               `SELECT id, compute_allocation_id, 
compute_allocation_resource_id
+               `SELECT id, compute_allocation_id, 
compute_allocation_resource_id, resource_amount, resource_time
                 FROM compute_allocation_resource_mappings
                 WHERE compute_allocation_id = ? AND 
compute_allocation_resource_id = ?`,
                allocationID, resourceID)
@@ -87,9 +102,19 @@ func (s *mysqlComputeAllocationResourceMappingStore) 
FindAllocationsByResource(c
 func (s *mysqlComputeAllocationResourceMappingStore) Create(ctx 
context.Context, tx *sql.Tx, m *models.ComputeAllocationResourceMapping) error {
        _, err := tx.ExecContext(ctx,
                `INSERT INTO compute_allocation_resource_mappings
-                    (id, compute_allocation_id, compute_allocation_resource_id)
-                VALUES (?, ?, ?)`,
-               m.ID, m.ComputeAllocationID, m.ComputeAllocationResourceID)
+                    (id, compute_allocation_id, 
compute_allocation_resource_id, resource_amount, resource_time)
+                VALUES (?, ?, ?, ?, ?)`,
+               m.ID, m.ComputeAllocationID, m.ComputeAllocationResourceID, 
m.ResourceAmount, m.ResourceTime)
+       return err
+}
+
+func (s *mysqlComputeAllocationResourceMappingStore) Update(ctx 
context.Context, tx *sql.Tx, m *models.ComputeAllocationResourceMapping) error {
+       _, err := tx.ExecContext(ctx,
+               `UPDATE compute_allocation_resource_mappings
+                   SET resource_amount = ?,
+                       resource_time   = ?
+                 WHERE id = ?`,
+               m.ResourceAmount, m.ResourceTime, m.ID)
        return err
 }
 
diff --git a/internal/store/store.go b/internal/store/store.go
index 8066d87e8..b066df428 100644
--- a/internal/store/store.go
+++ b/internal/store/store.go
@@ -140,6 +140,8 @@ type ComputeAllocationResourceStore interface {
 // ComputeAllocationResourceMappingStore defines persistence operations for
 // the join table linking compute allocations and compute allocation resources.
 type ComputeAllocationResourceMappingStore interface {
+       // FindByID returns the mapping with the given ID, or nil if it does 
not exist.
+       FindByID(ctx context.Context, id string) 
(*models.ComputeAllocationResourceMapping, error)
        // FindByPair returns the mapping for a (allocation, resource) pair, or 
nil if absent.
        FindByPair(ctx context.Context, allocationID, resourceID string) 
(*models.ComputeAllocationResourceMapping, error)
        // FindResourcesByAllocation returns every resource attached to the 
given allocation.
@@ -148,6 +150,8 @@ type ComputeAllocationResourceMappingStore interface {
        FindAllocationsByResource(ctx context.Context, resourceID string) 
([]models.ComputeAllocation, error)
        // Create inserts a new mapping within the provided transaction.
        Create(ctx context.Context, tx *sql.Tx, m 
*models.ComputeAllocationResourceMapping) error
+       // Update replaces mutable fields of an existing mapping within the 
provided transaction.
+       Update(ctx context.Context, tx *sql.Tx, m 
*models.ComputeAllocationResourceMapping) error
        // DeleteByPair removes the mapping for a (allocation, resource) pair 
within the provided transaction.
        DeleteByPair(ctx context.Context, tx *sql.Tx, allocationID, resourceID 
string) error
 }
diff --git a/pkg/models/allocation.go b/pkg/models/allocation.go
index ca95595b5..582bce606 100644
--- a/pkg/models/allocation.go
+++ b/pkg/models/allocation.go
@@ -33,17 +33,21 @@ type ComputeAllocation struct {
        EndTime          time.Time        `json:"end_time"           
db:"end_time"`
 }
 
+// Typically store the a paritition information
 type ComputeAllocationResource struct {
        ID             string `json:"id"              db:"id"`
-       Name           string `json:"name"            db:"name"`            // 
resource / partition name, e.g., "cpu", "gpu", etc.
-       ResourceType   string `json:"resource_type"   db:"resource_type"`   // 
GrpTRES, GrpTRESMins
-       ResourceAmount int64  `json:"resource_amount" db:"resource_amount"` // 
Number of CPUs, GPUs, time in minutes, or other unit depending on the resource 
type.
+       Name           string `json:"name"            db:"name"`            // 
resource / partition name, e.g., "cpu-01", "gpu-01", "gpu-interactive", etc.
+       ResourceType   string `json:"resource_type"   db:"resource_type"`   // 
cpu, gpu
+       ResourceAmount int64  `json:"resource_amount" db:"resource_amount"` // 
Number of CPUs, GPUs.
 }
 
+// Store the association amount for a parition and allocation
 type ComputeAllocationResourceMapping struct {
        ID                          string `json:"id"                           
  db:"id"`
        ComputeAllocationID         string `json:"compute_allocation_id"        
  db:"compute_allocation_id"`
        ComputeAllocationResourceID string 
`json:"compute_allocation_resource_id" db:"compute_allocation_resource_id"`
+       ResourceAmount              int64  `json:"resource_amount"              
  db:"resource_amount"` // Amount of the resource allocated to this allocation 
(e.g., number of CPUs, GPUs).
+       ResourceTime                int64  `json:"resource_time"                
  db:"resource_time"`   // Wall-clock time in minutes that the allocated amount 
is granted for.
 }
 
 type ComputeAllocationResourceRate struct {
diff --git a/pkg/service/compute_allocation_resource_mapping.go 
b/pkg/service/compute_allocation_resource_mapping.go
index d0c96014c..ce5f4ef18 100644
--- a/pkg/service/compute_allocation_resource_mapping.go
+++ b/pkg/service/compute_allocation_resource_mapping.go
@@ -27,16 +27,23 @@ import (
 )
 
 // AttachResourceToAllocation links a compute allocation resource to a compute
-// allocation. Both entities must already exist. The link is idempotent — if
-// the same (allocation, resource) pair is already linked, ErrAlreadyExists is
-// returned.
-func (s *Service) AttachResourceToAllocation(ctx context.Context, 
allocationID, resourceID string) (*models.ComputeAllocationResourceMapping, 
error) {
+// allocation, recording the resource amount and wall-clock time granted to
+// the allocation. Both entities must already exist. The link is idempotent —
+// if the same (allocation, resource) pair is already linked, ErrAlreadyExists
+// is returned.
+func (s *Service) AttachResourceToAllocation(ctx context.Context, 
allocationID, resourceID string, resourceAmount, resourceTime int64) 
(*models.ComputeAllocationResourceMapping, error) {
        if allocationID == "" {
                return nil, fmt.Errorf("%w: compute_allocation_id is required", 
ErrInvalidInput)
        }
        if resourceID == "" {
                return nil, fmt.Errorf("%w: compute_allocation_resource_id is 
required", ErrInvalidInput)
        }
+       if resourceAmount < 0 {
+               return nil, fmt.Errorf("%w: resource_amount must be 
non-negative", ErrInvalidInput)
+       }
+       if resourceTime < 0 {
+               return nil, fmt.Errorf("%w: resource_time must be 
non-negative", ErrInvalidInput)
+       }
 
        if alloc, err := s.allocs.FindByID(ctx, allocationID); err != nil {
                return nil, fmt.Errorf("lookup compute allocation: %w", err)
@@ -60,6 +67,8 @@ func (s *Service) AttachResourceToAllocation(ctx 
context.Context, allocationID,
                ID:                          newID(),
                ComputeAllocationID:         allocationID,
                ComputeAllocationResourceID: resourceID,
+               ResourceAmount:              resourceAmount,
+               ResourceTime:                resourceTime,
        }
        if err := s.inTx(ctx, func(tx *sql.Tx) error {
                return s.resourceMappings.Create(ctx, tx, mapping)
@@ -71,6 +80,39 @@ func (s *Service) AttachResourceToAllocation(ctx 
context.Context, allocationID,
        return mapping, nil
 }
 
+// UpdateAllocationResourceMapping updates the resource_amount and
+// resource_time recorded against an existing (allocation, resource) mapping.
+func (s *Service) UpdateAllocationResourceMapping(ctx context.Context, 
allocationID, resourceID string, resourceAmount, resourceTime int64) 
(*models.ComputeAllocationResourceMapping, error) {
+       if allocationID == "" || resourceID == "" {
+               return nil, fmt.Errorf("%w: allocation and resource ids are 
required", ErrInvalidInput)
+       }
+       if resourceAmount < 0 {
+               return nil, fmt.Errorf("%w: resource_amount must be 
non-negative", ErrInvalidInput)
+       }
+       if resourceTime < 0 {
+               return nil, fmt.Errorf("%w: resource_time must be 
non-negative", ErrInvalidInput)
+       }
+
+       existing, err := s.resourceMappings.FindByPair(ctx, allocationID, 
resourceID)
+       if err != nil {
+               return nil, fmt.Errorf("lookup mapping: %w", err)
+       }
+       if existing == nil {
+               return nil, ErrNotFound
+       }
+
+       existing.ResourceAmount = resourceAmount
+       existing.ResourceTime = resourceTime
+       if err := s.inTx(ctx, func(tx *sql.Tx) error {
+               return s.resourceMappings.Update(ctx, tx, existing)
+       }); err != nil {
+               return nil, fmt.Errorf("update allocation resource mapping: 
%w", err)
+       }
+
+       s.eventBus.Publish(events.ComputeAllocationResourceMappingUpdateEvent, 
existing)
+       return existing, nil
+}
+
 // DetachResourceFromAllocation removes the link between a compute allocation
 // and a compute allocation resource. Returns ErrNotFound when no such mapping
 // exists.

Reply via email to