Skip to content

Commit baefa29

Browse files
authored
Merge pull request #58 from netdata/feat/node-dynamic-membership
add node rule based room assignment option
2 parents e5e9294 + 17023d6 commit baefa29

File tree

10 files changed

+571
-36
lines changed

10 files changed

+571
-36
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.3.0
2+
3+
FEATURES:
4+
5+
- add Node Rule-Based Room Assignment option to the `netdata_node_room_member` resource
6+
17
## 0.2.2
28

39
FEATURES:

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ This provider allows you to install and manage Netdata Cloud resources using Ter
66

77
## Contents
88

9-
* [Requirements](#requirements)
10-
* [Getting Started](#getting-started)
9+
- [Terraform Provider for Netdata Cloud](#terraform-provider-for-netdata-cloud)
10+
- [Contents](#contents)
11+
- [Requirements](#requirements)
12+
- [Getting Started](#getting-started)
1113

1214
## Requirements
1315

docs/resources/node_room_member.md

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
page_title: "netdata_node_room_member Resource - terraform-provider-netdata"
44
subcategory: ""
55
description: |-
6-
Provides a Netdata Cloud Node Room Member resource. Use this resource to manage node membership to the room in the selected space, only reachable nodes can be added to the room.
7-
This resource is useful in the case of Netdata Streaming and Replication https://learn.netdata.cloud/docs/observability-centralization-points/metrics-centralization-points/ when you want to spread
8-
the Netdata child agents across different rooms because by default all of them end in the same room like the Netdata parent.
6+
Provides a Netdata Cloud Node Room Member resource. Use this resource to manage node membership to the room in the selected space.
7+
There are two options to add nodes to the room:
8+
providing the node names directly, but only reachable nodes will be added to the room, use node_names attribute for thiscreating rules that will automatically add nodes to the room based on the rule, use rule block for this
99
---
1010

1111
# netdata_node_room_member (Resource)
1212

13-
Provides a Netdata Cloud Node Room Member resource. Use this resource to manage node membership to the room in the selected space, only reachable nodes can be added to the room.
14-
This resource is useful in the case of [Netdata Streaming and Replication](https://learn.netdata.cloud/docs/observability-centralization-points/metrics-centralization-points/) when you want to spread
15-
the Netdata child agents across different rooms because by default all of them end in the same room like the Netdata parent.
13+
Provides a Netdata Cloud Node Room Member resource. Use this resource to manage node membership to the room in the selected space.
14+
There are two options to add nodes to the room:
15+
- providing the node names directly, but only reachable nodes will be added to the room, use node_names attribute for this
16+
- creating rules that will automatically add nodes to the room based on the rule, use rule block for this
1617

1718
## Example Usage
1819

@@ -24,6 +25,22 @@ resource "netdata_node_room_member" "test" {
2425
"node1",
2526
"node2"
2627
]
28+
rule {
29+
action = "INCLUDE"
30+
description = "Description of the rule"
31+
clause {
32+
label = "role"
33+
operator = "equals"
34+
value = "parent"
35+
negate = false
36+
}
37+
clause {
38+
label = "environment"
39+
operator = "equals"
40+
value = "production"
41+
negate = false
42+
}
43+
}
2744
}
2845
```
2946

@@ -32,9 +49,39 @@ resource "netdata_node_room_member" "test" {
3249

3350
### Required
3451

35-
- `node_names` (List of String) List of node names to add to the room
36-
- `room_id` (String) The Room ID of the space
37-
- `space_id` (String) Space ID of the member
52+
- `room_id` (String) The Room ID of the space.
53+
- `space_id` (String) Space ID of the member.
54+
55+
### Optional
56+
57+
- `node_names` (List of String) List of node names to add to the room. At least one node name is required.
58+
- `rule` (Block List) The node rule to apply to the room. The logical relation between multiple rules is OR. More info [here](https://learn.netdata.cloud/docs/netdata-cloud/spaces-and-rooms/node-rule-based-room-assignment). (see [below for nested schema](#nestedblock--rule))
59+
60+
<a id="nestedblock--rule"></a>
61+
### Nested Schema for `rule`
62+
63+
Required:
64+
65+
- `action` (String) Determines whether matching nodes will be included or excluded from the room. EXCLUDE action always takes precedence against INCLUDE. Valid values: INCLUDE or EXCLUDE.
66+
67+
Optional:
68+
69+
- `clause` (Block List) The clause to apply to the rule. The logical relation between multiple clauses is AND. It should be a least one clause. (see [below for nested schema](#nestedblock--rule--clause))
70+
- `description` (String) The description of the rule.
71+
72+
Read-Only:
73+
74+
- `id` (String) The ID of the rule.
75+
76+
<a id="nestedblock--rule--clause"></a>
77+
### Nested Schema for `rule.clause`
78+
79+
Required:
80+
81+
- `label` (String) The host label to check.
82+
- `negate` (Boolean) Negate the clause.
83+
- `operator` (String) Operator to compare. Valid values: equals, starts_with, ends_with, contains.
84+
- `value` (String) The value to compare against.
3885

3986
## Import
4087

examples/complete/main.tf

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,49 @@ terraform {
99

1010
provider "netdata" {}
1111

12+
resource "netdata_node_room_member" "new" {
13+
room_id = netdata_room.test.id
14+
space_id = netdata_space.test.id
15+
16+
node_names = [
17+
"node1",
18+
"node2"
19+
]
20+
21+
rule {
22+
action = "INCLUDE"
23+
description = "Description of the rule"
24+
clause {
25+
label = "role"
26+
operator = "equals"
27+
value = "parent"
28+
negate = false
29+
}
30+
clause {
31+
label = "environment"
32+
operator = "equals"
33+
value = "production"
34+
negate = false
35+
}
36+
}
37+
rule {
38+
action = "EXCLUDE"
39+
description = "Description of the rule"
40+
clause {
41+
label = "role"
42+
operator = "equals"
43+
value = "parent"
44+
negate = true
45+
}
46+
clause {
47+
label = "environment"
48+
operator = "contains"
49+
value = "production"
50+
negate = false
51+
}
52+
}
53+
}
54+
1255
resource "netdata_space" "test" {
1356
name = "MyTestingSpace"
1457
description = "Created by Terraform"

examples/resources/netdata_node_room_member/resource.tf

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,20 @@ resource "netdata_node_room_member" "test" {
55
"node1",
66
"node2"
77
]
8+
rule {
9+
action = "INCLUDE"
10+
description = "Description of the rule"
11+
clause {
12+
label = "role"
13+
operator = "equals"
14+
value = "parent"
15+
negate = false
16+
}
17+
clause {
18+
label = "environment"
19+
operator = "equals"
20+
value = "production"
21+
negate = false
22+
}
23+
}
824
}

internal/client/client.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ import (
1010
)
1111

1212
var (
13-
ErrNotFound = errors.New("not found")
14-
ErrSpaceIDRequired = errors.New("spaceID is required")
15-
ErrChannelIDRequired = errors.New("channelID is required")
16-
ErrRoomIDRequired = errors.New("roomID is required")
17-
ErrMemberIDRequired = errors.New("memberID is required")
18-
ErrNodeID = errors.New("nodeID is required")
13+
ErrNotFound = errors.New("not found")
14+
ErrSpaceIDRequired = errors.New("spaceID is required")
15+
ErrChannelIDRequired = errors.New("channelID is required")
16+
ErrRoomIDRequired = errors.New("roomID is required")
17+
ErrMemberIDRequired = errors.New("memberID is required")
18+
ErrNodeID = errors.New("nodeID is required")
19+
ErrNodeMembershipIDRequired = errors.New("nodeMembershipID is required")
20+
ErrNodeMembershipActionRequired = errors.New("nodeMembershipAction is required")
1921
)
2022

2123
type Client struct {

internal/client/models.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package client
22

3-
import "encoding/json"
3+
import (
4+
"encoding/json"
5+
6+
"github.com/google/uuid"
7+
)
48

59
type SpaceInfo struct {
610
ID string `json:"id"`
@@ -84,3 +88,17 @@ type RoomNode struct {
8488
NodeName string `json:"nm"`
8589
State string `json:"state"`
8690
}
91+
type NodeMembershipRule struct {
92+
ID uuid.UUID `json:"id"`
93+
SpaceID uuid.UUID `json:"spaceID"`
94+
RoomID uuid.UUID `json:"roomID"`
95+
Clauses []NodeMembershipClause `json:"clauses"`
96+
Action string `json:"action"`
97+
Description string `json:"description"`
98+
}
99+
type NodeMembershipClause struct {
100+
Label string `json:"label"`
101+
Operator string `json:"operator"`
102+
Value string `json:"value"`
103+
Negate bool `json:"negate"`
104+
}

internal/client/node_room_member.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,152 @@ func (c *Client) GetAllNodes(spaceID string) (*RoomNodes, error) {
5858
return roomNodes, nil
5959
}
6060

61+
func (c *Client) ListNodeMembershipRules(spaceID, roomID string) ([]NodeMembershipRule, error) {
62+
if spaceID == "" {
63+
return nil, ErrSpaceIDRequired
64+
}
65+
if roomID == "" {
66+
return nil, ErrRoomIDRequired
67+
}
68+
69+
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v3/spaces/%s/rooms/%s/node-membership-rules", c.HostURL, spaceID, roomID), nil)
70+
if err != nil {
71+
return nil, err
72+
}
73+
74+
var nodeMembershipRule []NodeMembershipRule
75+
76+
err = c.doRequestUnmarshal(req, &nodeMembershipRule)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
return nodeMembershipRule, nil
82+
}
83+
84+
func (c *Client) GetNodeMembershipRule(spaceID, roomID, nodeMembershipID string) (*NodeMembershipRule, error) {
85+
if spaceID == "" {
86+
return nil, ErrSpaceIDRequired
87+
}
88+
if roomID == "" {
89+
return nil, ErrRoomIDRequired
90+
}
91+
if nodeMembershipID == "" {
92+
return nil, ErrNodeMembershipIDRequired
93+
}
94+
95+
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v3/spaces/%s/rooms/%s/node-membership-rules/%s", c.HostURL, spaceID, roomID, nodeMembershipID), nil)
96+
if err != nil {
97+
return nil, err
98+
}
99+
100+
var nodeMembershipRule NodeMembershipRule
101+
102+
err = c.doRequestUnmarshal(req, &nodeMembershipRule)
103+
if err != nil {
104+
return nil, err
105+
}
106+
107+
return &nodeMembershipRule, nil
108+
}
109+
110+
func (c *Client) CreateNodeMembershipRule(spaceID, roomID, action, description string, clauses []NodeMembershipClause) (*NodeMembershipRule, error) {
111+
if spaceID == "" {
112+
return nil, ErrSpaceIDRequired
113+
}
114+
if roomID == "" {
115+
return nil, ErrRoomIDRequired
116+
}
117+
if action == "" {
118+
return nil, ErrNodeMembershipActionRequired
119+
}
120+
121+
reqBody, err := json.Marshal(map[string]interface{}{
122+
"action": action,
123+
"description": description,
124+
"clauses": clauses,
125+
})
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v3/spaces/%s/rooms/%s/node-membership-rules", c.HostURL, spaceID, roomID), bytes.NewReader(reqBody))
131+
if err != nil {
132+
return nil, err
133+
}
134+
135+
var nodeMembershipRule NodeMembershipRule
136+
137+
err = c.doRequestUnmarshal(req, &nodeMembershipRule)
138+
if err != nil {
139+
return nil, err
140+
}
141+
142+
return &nodeMembershipRule, nil
143+
}
144+
145+
func (c *Client) UpdateNodeMembershipRule(spaceID, roomID, nodeMembershipID, action, description string, clauses []NodeMembershipClause) (*NodeMembershipRule, error) {
146+
if spaceID == "" {
147+
return nil, ErrSpaceIDRequired
148+
}
149+
if roomID == "" {
150+
return nil, ErrRoomIDRequired
151+
}
152+
if nodeMembershipID == "" {
153+
return nil, ErrNodeMembershipIDRequired
154+
}
155+
if action == "" {
156+
return nil, ErrNodeMembershipActionRequired
157+
}
158+
159+
reqBody, err := json.Marshal(map[string]interface{}{
160+
"action": action,
161+
"description": description,
162+
"clauses": clauses,
163+
})
164+
if err != nil {
165+
return nil, err
166+
}
167+
168+
req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/v3/spaces/%s/rooms/%s/node-membership-rules/%s", c.HostURL, spaceID, roomID, nodeMembershipID), bytes.NewReader(reqBody))
169+
if err != nil {
170+
return nil, err
171+
}
172+
173+
var nodeMembershipRule NodeMembershipRule
174+
175+
err = c.doRequestUnmarshal(req, &nodeMembershipRule)
176+
if err != nil {
177+
return nil, err
178+
}
179+
180+
return &nodeMembershipRule, nil
181+
}
182+
183+
func (c *Client) DeleteNodeMembershipRule(spaceID, roomID, nodeMembershipID string) error {
184+
if spaceID == "" {
185+
return ErrSpaceIDRequired
186+
}
187+
if roomID == "" {
188+
return ErrRoomIDRequired
189+
}
190+
if nodeMembershipID == "" {
191+
return ErrNodeMembershipIDRequired
192+
}
193+
194+
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/v3/spaces/%s/rooms/%s/node-membership-rules/%s", c.HostURL, spaceID, roomID, nodeMembershipID), nil)
195+
if err != nil {
196+
return err
197+
}
198+
199+
_, err = c.doRequest(req)
200+
if err != nil {
201+
return err
202+
}
203+
204+
return nil
205+
}
206+
61207
func (c *Client) CreateNodeRoomMember(spaceID, roomID, nodeID string) error {
62208
if spaceID == "" {
63209
return ErrSpaceIDRequired

0 commit comments

Comments
 (0)