@@ -1461,3 +1461,201 @@ func updateInstanceAttestationStatusIndicator(
14611461 _ , err := inv_testing .TestClients [inv_testing .RMClient ].Update (ctx , instanceID , fieldMask , updateRes )
14621462 require .NoError (t , err )
14631463}
1464+
1465+ //nolint:funlen // Test functions are long but necessary to test all the cases.
1466+ func TestHost_PatchAmtControlModeValidation (t * testing.T ) {
1467+ mockedClient := newMockedInventoryTestClient ()
1468+ server := inv_server.InventorygRPCServer {InvClient : mockedClient }
1469+
1470+ cases := []struct {
1471+ name string
1472+ mocks func () []* mock.Call
1473+ ctx context.Context
1474+ req * restv1.PatchHostRequest
1475+ wantErr bool
1476+ description string
1477+ }{
1478+ {
1479+ name : "Block amtControlMode change when both states are PROVISIONED" ,
1480+ mocks : func () []* mock.Call {
1481+ currentHost := & inv_computev1.HostResource {
1482+ ResourceId : "host-12345678" ,
1483+ DesiredAmtState : inv_computev1 .AmtState_AMT_STATE_PROVISIONED ,
1484+ CurrentAmtState : inv_computev1 .AmtState_AMT_STATE_PROVISIONED ,
1485+ AmtControlMode : inv_computev1 .AmtControlMode_AMT_CONTROL_MODE_CCM ,
1486+ }
1487+ return []* mock.Call {
1488+ mockedClient .On ("Get" , mock .Anything , "host-12345678" ).
1489+ Return (& inventory.GetResourceResponse {
1490+ Resource : & inventory.Resource {
1491+ Resource : & inventory.Resource_Host {Host : currentHost },
1492+ },
1493+ }, nil ).Once (),
1494+ }
1495+ },
1496+ ctx : context .Background (),
1497+ req : & restv1.PatchHostRequest {
1498+ ResourceId : "host-12345678" ,
1499+ Host : & computev1.HostResource {
1500+ AmtControlMode : computev1 .AmtControlMode_AMT_CONTROL_MODE_ACM ,
1501+ },
1502+ FieldMask : & fieldmaskpb.FieldMask {
1503+ Paths : []string {computev1 .HostResourceFieldAmtControlMode },
1504+ },
1505+ },
1506+ wantErr : true ,
1507+ description : "Should reject amtControlMode change when host is fully provisioned" ,
1508+ },
1509+ {
1510+ name : "Allow amtControlMode change when desiredAmtState is not PROVISIONED" ,
1511+ mocks : func () []* mock.Call {
1512+ currentHost := & inv_computev1.HostResource {
1513+ ResourceId : "host-12345678" ,
1514+ DesiredAmtState : inv_computev1 .AmtState_AMT_STATE_UNPROVISIONED ,
1515+ CurrentAmtState : inv_computev1 .AmtState_AMT_STATE_PROVISIONED ,
1516+ AmtControlMode : inv_computev1 .AmtControlMode_AMT_CONTROL_MODE_CCM ,
1517+ }
1518+ return []* mock.Call {
1519+ mockedClient .On ("Get" , mock .Anything , "host-12345678" ).
1520+ Return (& inventory.GetResourceResponse {
1521+ Resource : & inventory.Resource {
1522+ Resource : & inventory.Resource_Host {Host : currentHost },
1523+ },
1524+ }, nil ).Once (),
1525+ mockedClient .On ("Update" , mock .Anything , "host-12345678" , mock .Anything , mock .Anything ).
1526+ Return (& inventory.Resource {
1527+ Resource : & inventory.Resource_Host {Host : currentHost },
1528+ }, nil ).Once (),
1529+ }
1530+ },
1531+ ctx : context .Background (),
1532+ req : & restv1.PatchHostRequest {
1533+ ResourceId : "host-12345678" ,
1534+ Host : & computev1.HostResource {
1535+ AmtControlMode : computev1 .AmtControlMode_AMT_CONTROL_MODE_ACM ,
1536+ },
1537+ FieldMask : & fieldmaskpb.FieldMask {
1538+ Paths : []string {computev1 .HostResourceFieldAmtControlMode },
1539+ },
1540+ },
1541+ wantErr : false ,
1542+ description : "Should allow amtControlMode change when desiredAmtState is not PROVISIONED" ,
1543+ },
1544+ {
1545+ name : "Allow amtControlMode change when currentAmtState is not PROVISIONED" ,
1546+ mocks : func () []* mock.Call {
1547+ currentHost := & inv_computev1.HostResource {
1548+ ResourceId : "host-12345678" ,
1549+ DesiredAmtState : inv_computev1 .AmtState_AMT_STATE_PROVISIONED ,
1550+ CurrentAmtState : inv_computev1 .AmtState_AMT_STATE_UNPROVISIONED ,
1551+ AmtControlMode : inv_computev1 .AmtControlMode_AMT_CONTROL_MODE_CCM ,
1552+ }
1553+ return []* mock.Call {
1554+ mockedClient .On ("Get" , mock .Anything , "host-12345678" ).
1555+ Return (& inventory.GetResourceResponse {
1556+ Resource : & inventory.Resource {
1557+ Resource : & inventory.Resource_Host {Host : currentHost },
1558+ },
1559+ }, nil ).Once (),
1560+ mockedClient .On ("Update" , mock .Anything , "host-12345678" , mock .Anything , mock .Anything ).
1561+ Return (& inventory.Resource {
1562+ Resource : & inventory.Resource_Host {Host : currentHost },
1563+ }, nil ).Once (),
1564+ }
1565+ },
1566+ ctx : context .Background (),
1567+ req : & restv1.PatchHostRequest {
1568+ ResourceId : "host-12345678" ,
1569+ Host : & computev1.HostResource {
1570+ AmtControlMode : computev1 .AmtControlMode_AMT_CONTROL_MODE_ACM ,
1571+ },
1572+ FieldMask : & fieldmaskpb.FieldMask {
1573+ Paths : []string {computev1 .HostResourceFieldAmtControlMode },
1574+ },
1575+ },
1576+ wantErr : false ,
1577+ description : "Should allow amtControlMode change when currentAmtState is not PROVISIONED" ,
1578+ },
1579+ {
1580+ name : "Allow patch without amtControlMode in field mask" ,
1581+ mocks : func () []* mock.Call {
1582+ currentHost := & inv_computev1.HostResource {
1583+ ResourceId : "host-12345678" ,
1584+ DesiredAmtState : inv_computev1 .AmtState_AMT_STATE_PROVISIONED ,
1585+ CurrentAmtState : inv_computev1 .AmtState_AMT_STATE_PROVISIONED ,
1586+ AmtControlMode : inv_computev1 .AmtControlMode_AMT_CONTROL_MODE_CCM ,
1587+ Name : "updated-host" ,
1588+ }
1589+ return []* mock.Call {
1590+ mockedClient .On ("Update" , mock .Anything , "host-12345678" , mock .Anything , mock .Anything ).
1591+ Return (& inventory.Resource {
1592+ Resource : & inventory.Resource_Host {Host : currentHost },
1593+ }, nil ).Once (),
1594+ }
1595+ },
1596+ ctx : context .Background (),
1597+ req : & restv1.PatchHostRequest {
1598+ ResourceId : "host-12345678" ,
1599+ Host : & computev1.HostResource {
1600+ Name : "updated-host" ,
1601+ },
1602+ FieldMask : & fieldmaskpb.FieldMask {
1603+ Paths : []string {"name" },
1604+ },
1605+ },
1606+ wantErr : false ,
1607+ description : "Should allow patch when amtControlMode is not in field mask even if host is provisioned" ,
1608+ },
1609+ {
1610+ name : "Allow amtControlMode change when both states are UNSPECIFIED" ,
1611+ mocks : func () []* mock.Call {
1612+ currentHost := & inv_computev1.HostResource {
1613+ ResourceId : "host-12345678" ,
1614+ DesiredAmtState : inv_computev1 .AmtState_AMT_STATE_UNSPECIFIED ,
1615+ CurrentAmtState : inv_computev1 .AmtState_AMT_STATE_UNSPECIFIED ,
1616+ AmtControlMode : inv_computev1 .AmtControlMode_AMT_CONTROL_MODE_UNSPECIFIED ,
1617+ }
1618+ return []* mock.Call {
1619+ mockedClient .On ("Get" , mock .Anything , "host-12345678" ).
1620+ Return (& inventory.GetResourceResponse {
1621+ Resource : & inventory.Resource {
1622+ Resource : & inventory.Resource_Host {Host : currentHost },
1623+ },
1624+ }, nil ).Once (),
1625+ mockedClient .On ("Update" , mock .Anything , "host-12345678" , mock .Anything , mock .Anything ).
1626+ Return (& inventory.Resource {
1627+ Resource : & inventory.Resource_Host {Host : currentHost },
1628+ }, nil ).Once (),
1629+ }
1630+ },
1631+ ctx : context .Background (),
1632+ req : & restv1.PatchHostRequest {
1633+ ResourceId : "host-12345678" ,
1634+ Host : & computev1.HostResource {
1635+ AmtControlMode : computev1 .AmtControlMode_AMT_CONTROL_MODE_ACM ,
1636+ },
1637+ FieldMask : & fieldmaskpb.FieldMask {
1638+ Paths : []string {computev1 .HostResourceFieldAmtControlMode },
1639+ },
1640+ },
1641+ wantErr : false ,
1642+ description : "Should allow amtControlMode change when both states are UNSPECIFIED" ,
1643+ },
1644+ }
1645+
1646+ for _ , tc := range cases {
1647+ t .Run (tc .name , func (t * testing.T ) {
1648+ if tc .mocks != nil {
1649+ tc .mocks ()
1650+ }
1651+
1652+ reply , err := server .PatchHost (tc .ctx , tc .req )
1653+ if tc .wantErr {
1654+ assert .Error (t , err , tc .description )
1655+ return
1656+ }
1657+ assert .NoError (t , err , tc .description )
1658+ assert .NotNil (t , reply , tc .description )
1659+ })
1660+ }
1661+ }
0 commit comments