Skip to content

Commit

Permalink
Merge pull request #58 from netdata/feat/node-dynamic-membership
Browse files Browse the repository at this point in the history
add node rule based room assignment option
  • Loading branch information
witalisoft authored Jan 20, 2025
2 parents e5e9294 + 17023d6 commit baefa29
Show file tree
Hide file tree
Showing 10 changed files with 571 additions and 36 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.3.0

FEATURES:

- add Node Rule-Based Room Assignment option to the `netdata_node_room_member` resource

## 0.2.2

FEATURES:
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ This provider allows you to install and manage Netdata Cloud resources using Ter

## Contents

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

## Requirements

Expand Down
65 changes: 56 additions & 9 deletions docs/resources/node_room_member.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
page_title: "netdata_node_room_member Resource - terraform-provider-netdata"
subcategory: ""
description: |-
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.
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
the Netdata child agents across different rooms because by default all of them end in the same room like the Netdata parent.
Provides a Netdata Cloud Node Room Member resource. Use this resource to manage node membership to the room in the selected space.
There are two options to add nodes to the room:
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
---

# netdata_node_room_member (Resource)

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.
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
the Netdata child agents across different rooms because by default all of them end in the same room like the Netdata parent.
Provides a Netdata Cloud Node Room Member resource. Use this resource to manage node membership to the room in the selected space.
There are two options to add nodes to the room:
- providing the node names directly, but only reachable nodes will be added to the room, use node_names attribute for this
- creating rules that will automatically add nodes to the room based on the rule, use rule block for this

## Example Usage

Expand All @@ -24,6 +25,22 @@ resource "netdata_node_room_member" "test" {
"node1",
"node2"
]
rule {
action = "INCLUDE"
description = "Description of the rule"
clause {
label = "role"
operator = "equals"
value = "parent"
negate = false
}
clause {
label = "environment"
operator = "equals"
value = "production"
negate = false
}
}
}
```

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

### Required

- `node_names` (List of String) List of node names to add to the room
- `room_id` (String) The Room ID of the space
- `space_id` (String) Space ID of the member
- `room_id` (String) The Room ID of the space.
- `space_id` (String) Space ID of the member.

### Optional

- `node_names` (List of String) List of node names to add to the room. At least one node name is required.
- `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))

<a id="nestedblock--rule"></a>
### Nested Schema for `rule`

Required:

- `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.

Optional:

- `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))
- `description` (String) The description of the rule.

Read-Only:

- `id` (String) The ID of the rule.

<a id="nestedblock--rule--clause"></a>
### Nested Schema for `rule.clause`

Required:

- `label` (String) The host label to check.
- `negate` (Boolean) Negate the clause.
- `operator` (String) Operator to compare. Valid values: equals, starts_with, ends_with, contains.
- `value` (String) The value to compare against.

## Import

Expand Down
43 changes: 43 additions & 0 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,49 @@ terraform {

provider "netdata" {}

resource "netdata_node_room_member" "new" {
room_id = netdata_room.test.id
space_id = netdata_space.test.id

node_names = [
"node1",
"node2"
]

rule {
action = "INCLUDE"
description = "Description of the rule"
clause {
label = "role"
operator = "equals"
value = "parent"
negate = false
}
clause {
label = "environment"
operator = "equals"
value = "production"
negate = false
}
}
rule {
action = "EXCLUDE"
description = "Description of the rule"
clause {
label = "role"
operator = "equals"
value = "parent"
negate = true
}
clause {
label = "environment"
operator = "contains"
value = "production"
negate = false
}
}
}

resource "netdata_space" "test" {
name = "MyTestingSpace"
description = "Created by Terraform"
Expand Down
16 changes: 16 additions & 0 deletions examples/resources/netdata_node_room_member/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,20 @@ resource "netdata_node_room_member" "test" {
"node1",
"node2"
]
rule {
action = "INCLUDE"
description = "Description of the rule"
clause {
label = "role"
operator = "equals"
value = "parent"
negate = false
}
clause {
label = "environment"
operator = "equals"
value = "production"
negate = false
}
}
}
14 changes: 8 additions & 6 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import (
)

var (
ErrNotFound = errors.New("not found")
ErrSpaceIDRequired = errors.New("spaceID is required")
ErrChannelIDRequired = errors.New("channelID is required")
ErrRoomIDRequired = errors.New("roomID is required")
ErrMemberIDRequired = errors.New("memberID is required")
ErrNodeID = errors.New("nodeID is required")
ErrNotFound = errors.New("not found")
ErrSpaceIDRequired = errors.New("spaceID is required")
ErrChannelIDRequired = errors.New("channelID is required")
ErrRoomIDRequired = errors.New("roomID is required")
ErrMemberIDRequired = errors.New("memberID is required")
ErrNodeID = errors.New("nodeID is required")
ErrNodeMembershipIDRequired = errors.New("nodeMembershipID is required")
ErrNodeMembershipActionRequired = errors.New("nodeMembershipAction is required")
)

type Client struct {
Expand Down
20 changes: 19 additions & 1 deletion internal/client/models.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package client

import "encoding/json"
import (
"encoding/json"

"github.com/google/uuid"
)

type SpaceInfo struct {
ID string `json:"id"`
Expand Down Expand Up @@ -84,3 +88,17 @@ type RoomNode struct {
NodeName string `json:"nm"`
State string `json:"state"`
}
type NodeMembershipRule struct {
ID uuid.UUID `json:"id"`
SpaceID uuid.UUID `json:"spaceID"`
RoomID uuid.UUID `json:"roomID"`
Clauses []NodeMembershipClause `json:"clauses"`
Action string `json:"action"`
Description string `json:"description"`
}
type NodeMembershipClause struct {
Label string `json:"label"`
Operator string `json:"operator"`
Value string `json:"value"`
Negate bool `json:"negate"`
}
146 changes: 146 additions & 0 deletions internal/client/node_room_member.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,152 @@ func (c *Client) GetAllNodes(spaceID string) (*RoomNodes, error) {
return roomNodes, nil
}

func (c *Client) ListNodeMembershipRules(spaceID, roomID string) ([]NodeMembershipRule, error) {
if spaceID == "" {
return nil, ErrSpaceIDRequired
}
if roomID == "" {
return nil, ErrRoomIDRequired
}

req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v3/spaces/%s/rooms/%s/node-membership-rules", c.HostURL, spaceID, roomID), nil)
if err != nil {
return nil, err
}

var nodeMembershipRule []NodeMembershipRule

err = c.doRequestUnmarshal(req, &nodeMembershipRule)
if err != nil {
return nil, err
}

return nodeMembershipRule, nil
}

func (c *Client) GetNodeMembershipRule(spaceID, roomID, nodeMembershipID string) (*NodeMembershipRule, error) {
if spaceID == "" {
return nil, ErrSpaceIDRequired
}
if roomID == "" {
return nil, ErrRoomIDRequired
}
if nodeMembershipID == "" {
return nil, ErrNodeMembershipIDRequired
}

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)
if err != nil {
return nil, err
}

var nodeMembershipRule NodeMembershipRule

err = c.doRequestUnmarshal(req, &nodeMembershipRule)
if err != nil {
return nil, err
}

return &nodeMembershipRule, nil
}

func (c *Client) CreateNodeMembershipRule(spaceID, roomID, action, description string, clauses []NodeMembershipClause) (*NodeMembershipRule, error) {
if spaceID == "" {
return nil, ErrSpaceIDRequired
}
if roomID == "" {
return nil, ErrRoomIDRequired
}
if action == "" {
return nil, ErrNodeMembershipActionRequired
}

reqBody, err := json.Marshal(map[string]interface{}{
"action": action,
"description": description,
"clauses": clauses,
})
if err != nil {
return nil, err
}

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))
if err != nil {
return nil, err
}

var nodeMembershipRule NodeMembershipRule

err = c.doRequestUnmarshal(req, &nodeMembershipRule)
if err != nil {
return nil, err
}

return &nodeMembershipRule, nil
}

func (c *Client) UpdateNodeMembershipRule(spaceID, roomID, nodeMembershipID, action, description string, clauses []NodeMembershipClause) (*NodeMembershipRule, error) {
if spaceID == "" {
return nil, ErrSpaceIDRequired
}
if roomID == "" {
return nil, ErrRoomIDRequired
}
if nodeMembershipID == "" {
return nil, ErrNodeMembershipIDRequired
}
if action == "" {
return nil, ErrNodeMembershipActionRequired
}

reqBody, err := json.Marshal(map[string]interface{}{
"action": action,
"description": description,
"clauses": clauses,
})
if err != nil {
return nil, err
}

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))
if err != nil {
return nil, err
}

var nodeMembershipRule NodeMembershipRule

err = c.doRequestUnmarshal(req, &nodeMembershipRule)
if err != nil {
return nil, err
}

return &nodeMembershipRule, nil
}

func (c *Client) DeleteNodeMembershipRule(spaceID, roomID, nodeMembershipID string) error {
if spaceID == "" {
return ErrSpaceIDRequired
}
if roomID == "" {
return ErrRoomIDRequired
}
if nodeMembershipID == "" {
return ErrNodeMembershipIDRequired
}

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)
if err != nil {
return err
}

_, err = c.doRequest(req)
if err != nil {
return err
}

return nil
}

func (c *Client) CreateNodeRoomMember(spaceID, roomID, nodeID string) error {
if spaceID == "" {
return ErrSpaceIDRequired
Expand Down
Loading

0 comments on commit baefa29

Please sign in to comment.