Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: agent patch resources #92

Merged
merged 4 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
88 changes: 35 additions & 53 deletions internal/provider/resources/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,6 @@ type agentResourceModel struct {
PCapMode types.String `tfsdk:"pcap_mode"`
}

// TODO: Switch this to use the Client Model when that becomes available
type AgentResponseModel struct {
UID string
Serial string
Name string
ModelNumber string
WifiMacAddress string
EthernetMacAddress string
Notes string
PCapMode string
}

// TODO: Switch this to use the Client Model when that becomes available
type AgentUpdateRequestModel struct {
Name string
Notes string
PCapMode string
}

func NewAgentResource() resource.Resource {
return &agentResource{}
}
Expand Down Expand Up @@ -142,7 +123,7 @@ func (r *agentResource) Read(
request := r.client.ConfigurationAPI.
AgentsGet(ctx).
Id(state.ID.ValueString())
sensorResponse, response, err := util.RetryFor429(request.Execute)
agentResponse, response, err := util.RetryFor429(request.Execute)
errorPresent, errorDetail := util.RaiseForStatus(response, err)

errorSummary := util.GenerateErrorSummary("read", "uxi_agent")
Expand All @@ -152,16 +133,16 @@ func (r *agentResource) Read(
return
}

if len(sensorResponse.Items) != 1 {
if len(agentResponse.Items) != 1 {
resp.State.RemoveResource(ctx)
return
}
sensor := sensorResponse.Items[0]
agent := agentResponse.Items[0]

state.ID = types.StringValue(sensor.Id)
state.Name = types.StringValue(sensor.Name)
state.Notes = types.StringPointerValue(sensor.Notes.Get())
state.PCapMode = types.StringPointerValue(sensor.PcapMode.Get())
state.ID = types.StringValue(agent.Id)
state.Name = types.StringValue(agent.Name)
state.Notes = types.StringPointerValue(agent.Notes.Get())
state.PCapMode = types.StringPointerValue(agent.PcapMode.Get())

diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
Expand All @@ -183,18 +164,35 @@ func (r *agentResource) Update(
return
}

// Update existing item
response := UpdateAgent(AgentUpdateRequestModel{
Name: plan.Name.ValueString(),
Notes: plan.Notes.ValueString(),
PCapMode: plan.PCapMode.ValueString(),
})
patchRequest := config_api_client.NewAgentsPatchRequest()
patchRequest.Name = plan.Name.ValueStringPointer()
if !plan.Notes.IsUnknown() {
patchRequest.Notes = plan.Notes.ValueStringPointer()
}
if !plan.PCapMode.IsUnknown() {
patchRequest.PcapMode = plan.PCapMode.ValueStringPointer()
}
request := r.client.ConfigurationAPI.
AgentsPatch(ctx, plan.ID.ValueString()).
AgentsPatchRequest(*patchRequest)
agent, response, err := util.RetryFor429(request.Execute)

// Update resource state with updated items
plan.ID = types.StringValue(response.UID)
plan.Name = types.StringValue(response.Name)
plan.Notes = types.StringValue(response.Notes)
plan.PCapMode = types.StringValue(response.PCapMode)
errorPresent, errorDetail := util.RaiseForStatus(response, err)

if errorPresent {
resp.Diagnostics.AddError(util.GenerateErrorSummary("update", "uxi_agent"), errorDetail)
return
}

// Update the state to match the plan (replace with response from client)
plan.ID = types.StringValue(agent.Id)
plan.Name = types.StringValue(agent.Name)
if agent.Notes.Get() != nil {
plan.Notes = types.StringValue(*agent.Notes.Get())
}
if agent.PcapMode.Get() != nil {
plan.PCapMode = types.StringValue(*agent.PcapMode.Get())
}

// Set state to fully populated data
diags = resp.State.Set(ctx, plan)
Expand Down Expand Up @@ -235,19 +233,3 @@ func (r *agentResource) ImportState(
) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

// Update the agent using the configuration-api client
var UpdateAgent = func(request AgentUpdateRequestModel) AgentResponseModel {
// TODO: Query the agent using the client

return AgentResponseModel{
UID: "mock_uid",
Serial: "mock_serial",
Name: request.Name,
ModelNumber: "mock_model_number",
WifiMacAddress: "mock_wifi_mac_address",
EthernetMacAddress: "mock_ethernet_mac_address",
Notes: request.Notes,
PCapMode: request.PCapMode,
}
}
1 change: 1 addition & 0 deletions internal/provider/resources/sensor.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ func (r *sensorResource) Update(
}

patchRequest := config_api_client.NewSensorsPatchRequest()
patchRequest.Name = plan.Name.ValueStringPointer()
if !plan.AddressNote.IsUnknown() {
patchRequest.AddressNote = plan.AddressNote.ValueStringPointer()
}
Expand Down
87 changes: 83 additions & 4 deletions test/mocked/resources/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"regexp"
"testing"

"github.com/aruba-uxi/terraform-provider-hpeuxi/internal/provider/resources"
"github.com/aruba-uxi/terraform-provider-hpeuxi/test/mocked/provider"
"github.com/aruba-uxi/terraform-provider-hpeuxi/test/mocked/util"
"github.com/h2non/gock"
Expand Down Expand Up @@ -90,9 +89,12 @@ func TestAgentResource(t *testing.T) {
),
1,
)
resources.UpdateAgent = func(request resources.AgentUpdateRequestModel) resources.AgentResponseModel {
return util.GenerateMockedAgentResponseModel("uid", "_2")
}
util.MockUpdateAgent(
"uid",
util.GenerateAgentRequestUpdateModel("_2"),
util.GenerateAgentResponseModel("uid", "_2"),
1,
)
// updated
util.MockGetAgent(
"uid",
Expand Down Expand Up @@ -175,6 +177,50 @@ func TestAgentResource429Handling(t *testing.T) {
},
),
},
// Update testing
{
PreConfig: func() {
// original
util.MockGetAgent(
"uid",
util.GeneratePaginatedResponse(
[]map[string]interface{}{util.GenerateAgentResponseModel("uid", "")},
),
1,
)
request429 = gock.New("https://test.api.capenetworks.com").
Patch("/networking-uxi/v1alpha1/agents").
Reply(429).
SetHeaders(util.RateLimitingHeaders)
util.MockUpdateAgent(
"uid",
util.GenerateAgentRequestUpdateModel("_2"),
util.GenerateAgentResponseModel("uid", "_2"),
1,
)
// updated
util.MockGetAgent(
"uid",
util.GeneratePaginatedResponse(
[]map[string]interface{}{util.GenerateAgentResponseModel("uid", "_2")},
),
1,
)
},
Config: provider.ProviderConfig + `
resource "uxi_agent" "my_agent" {
name = "name_2"
notes = "notes_2"
pcap_mode = "light_2"
}`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("uxi_agent.my_agent", "name", "name_2"),
func(s *terraform.State) error {
st.Assert(t, request429.Mock.Request().Counter, 0)
return nil
},
),
},
// Delete testing
{
PreConfig: func() {
Expand Down Expand Up @@ -292,6 +338,39 @@ func TestAgentResourceHttpErrorHandling(t *testing.T) {
resource.TestCheckResourceAttr("uxi_agent.my_agent", "id", "uid"),
),
},
// update 4xx
{
PreConfig: func() {
// original
util.MockGetAgent(
"uid",
util.GeneratePaginatedResponse(
[]map[string]interface{}{util.GenerateAgentResponseModel("uid", "")},
),
1,
)
// patch agent - with error
gock.New("https://test.api.capenetworks.com").
Patch("/networking-uxi/v1alpha1/agents/uid").
Reply(422).
JSON(map[string]interface{}{
"httpStatusCode": 422,
"errorCode": "HPE_GL_UXI_INVALID_PCAP_MODE_ERROR",
"message": "Unable to update agent - pcap_mode must be one the following ['light', 'full', 'off'].",
"debugId": "12312-123123-123123-1231212",
"type": "hpe.greenlake.uxi.invalid_pcap_mode",
})
},
Config: provider.ProviderConfig + `
resource "uxi_agent" "my_agent" {
name = "name_2"
notes = "notes_2"
pcap_mode = "light_2"
}`,
ExpectError: regexp.MustCompile(
`(?s)Unable to update agent - pcap_mode must be one the following \['light',\s*'full', 'off'\].\s*DebugID: 12312-123123-123123-1231212`,
),
},
// Delete 4xx
{
PreConfig: func() {
Expand Down
46 changes: 28 additions & 18 deletions test/mocked/util/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,24 @@ func GenerateSensorResponseModel(uid string, postfix string) map[string]interfac
"type": "networking-uxi/sensor",
}
}

func GenerateSensorRequestUpdateModel(postfix string) map[string]interface{} {
return map[string]interface{}{
"name": "name_2",
"name": "name" + postfix,
"addressNote": "address_note" + postfix,
"notes": "notes" + postfix,
"pcapMode": "light" + postfix,
}
}

func GenerateAgentRequestUpdateModel(postfix string) map[string]interface{} {
return map[string]interface{}{
"name": "name" + postfix,
"notes": "notes" + postfix,
"pcapMode": "light" + postfix,
}
}

func GenerateAgentResponseModel(uid string, postfix string) map[string]interface{} {
return map[string]interface{}{
"id": uid,
Expand All @@ -46,19 +55,6 @@ func GenerateAgentResponseModel(uid string, postfix string) map[string]interface
}
}

func GenerateMockedAgentResponseModel(uid string, postfix string) resources.AgentResponseModel {
return resources.AgentResponseModel{
UID: uid,
Serial: "serial" + postfix,
Name: "name" + postfix,
ModelNumber: "model_number" + postfix,
WifiMacAddress: "wifi_mac_address" + postfix,
EthernetMacAddress: "ethernet_mac_address" + postfix,
Notes: "notes" + postfix,
PCapMode: "light" + postfix,
}
}

func GenerateNonRootGroupResponseModel(
uid string,
nonReplacementFieldPostfix string,
Expand Down Expand Up @@ -262,6 +258,22 @@ func MockDeleteAgent(uid string, times int) {
Reply(204)
}

func MockUpdateAgent(
uid string,
request map[string]interface{},
response map[string]interface{},
times int,
) {
gock.New("https://test.api.capenetworks.com").
Patch("/networking-uxi/v1alpha1/agents/"+uid).
MatchHeader("Content-Type", "application/merge-patch+json").
MatchHeader("Authorization", "mock_token").
JSON(request).
Times(times).
Reply(200).
JSON(response)
}

func MockPostGroup(request map[string]interface{}, response map[string]interface{}, times int) {
gock.New("https://test.api.capenetworks.com").
Post("/networking-uxi/v1alpha1/groups").
Expand Down Expand Up @@ -324,13 +336,11 @@ func MockUpdateSensor(
response map[string]interface{},
times int,
) {
// body, _ := json.Marshal(request)
gock.New("https://test.api.capenetworks.com").
Patch("/networking-uxi/v1alpha1/sensors/"+uid).
// TODO: uncomment this once the patch endpoint uses correct header and body casing
// MatchHeader("Content-Type", "application/merge-patch+json").
MatchHeader("Content-Type", "application/merge-patch+json").
MatchHeader("Authorization", "mock_token").
// BodyString(string(body)).
JSON(request).
Times(times).
Reply(200).
JSON(response)
Expand Down
Loading