diff --git a/v2/devices.go b/v2/devices.go index f9ae664..1d7e1bd 100644 --- a/v2/devices.go +++ b/v2/devices.go @@ -72,6 +72,12 @@ type DevicePostureAttributes struct { Expiries map[string]Time `json:"expiries"` } +type DevicePostureAttributeRequest struct { + Value any `json:"value"` + Expiry Time `json:"expiry"` + Comment string `json:"comment"` +} + // Get gets the [Device] identified by deviceID. func (dr *DevicesResource) Get(ctx context.Context, deviceID string) (*Device, error) { req, err := dr.buildRequest(ctx, http.MethodGet, dr.buildURL("device", deviceID)) @@ -82,6 +88,7 @@ func (dr *DevicesResource) Get(ctx context.Context, deviceID string) (*Device, e return body[Device](dr, req) } +// GetPostureAttributes retrieves the posture attributes of the device identified by deviceID. func (dr *DevicesResource) GetPostureAttributes(ctx context.Context, deviceID string) (*DevicePostureAttributes, error) { req, err := dr.buildRequest(ctx, http.MethodGet, dr.buildURL("device", deviceID, "attributes")) if err != nil { @@ -153,6 +160,16 @@ func (dr *DevicesResource) SetTags(ctx context.Context, deviceID string, tags [] return dr.do(req, nil) } +// SetPostureAttribute sets the posture attribute of the device identified by deviceID. +func (dr *DevicesResource) SetPostureAttribute(ctx context.Context, deviceID, attributeKey string, request DevicePostureAttributeRequest) error { + req, err := dr.buildRequest(ctx, http.MethodPost, dr.buildURL("device", deviceID, "attributes", attributeKey), requestBody(request)) + if err != nil { + return err + } + + return dr.do(req, nil) +} + // DeviceKey type represents the properties of the key of an individual device within // the tailnet. type DeviceKey struct { diff --git a/v2/devices_test.go b/v2/devices_test.go index ec0cdf0..5786090 100644 --- a/v2/devices_test.go +++ b/v2/devices_test.go @@ -310,6 +310,32 @@ func TestClient_SetDeviceTags(t *testing.T) { assert.EqualValues(t, tags, body["tags"]) } +func TestClient_SetDevicePostureAttributes(t *testing.T) { + t.Parallel() + + client, server := NewTestHarness(t) + server.ResponseCode = http.StatusOK + server.ResponseBody = nil + + const deviceID = "test" + const attributeKey = "custom:test" + + setRequest := tsclient.DevicePostureAttributeRequest{ + Value: "value", + Expiry: tsclient.Time{time.Date(2022, 2, 10, 11, 50, 23, 0, time.UTC)}, + Comment: "test", + } + + assert.NoError(t, client.Devices().SetPostureAttribute(context.Background(), deviceID, attributeKey, setRequest)) + assert.EqualValues(t, http.MethodPost, server.Method) + assert.EqualValues(t, "/api/v2/device/"+deviceID+"/attributes/"+attributeKey, server.Path) + + var receivedRequest tsclient.DevicePostureAttributeRequest + err := json.Unmarshal(server.Body.Bytes(), &receivedRequest) + assert.NoError(t, err) + assert.EqualValues(t, setRequest, receivedRequest) +} + func TestClient_SetDeviceKey(t *testing.T) { t.Parallel() @@ -329,8 +355,8 @@ func TestClient_SetDeviceKey(t *testing.T) { var actual tsclient.DeviceKey assert.NoError(t, json.Unmarshal(server.Body.Bytes(), &actual)) assert.EqualValues(t, expected, actual) - } + func TestClient_SetDeviceIPv4Address(t *testing.T) { t.Parallel()