diff --git a/README.md b/README.md
index fb2b4bdb..2b1b6010 100644
--- a/README.md
+++ b/README.md
@@ -54,6 +54,8 @@ make docker-compose/docs
pre-commit run --show-diff-on-failure --color=always --all-files
```
+> **_Note_** that due to a [limitation of the tfplugindocs tool](https://github.com/hashicorp/terraform-plugin-docs/issues/28), some descriptions might not be automatically generated for nested fields. In this case, its necessary to generate the documentation manually by editing the template file - in the `templates` folder - corresponding to the resource/data-source.
+
> `pre-commit` can sometimes fail because your user is not the owner of the files in the `/docs` directory.
> To solve this problem, run the following command and re-run the `pre-commit run...` tried in the previous step:
diff --git a/cyral/data_source_cyral_sidecar_instance.go b/cyral/data_source_cyral_sidecar_instance.go
new file mode 100644
index 00000000..918f449c
--- /dev/null
+++ b/cyral/data_source_cyral_sidecar_instance.go
@@ -0,0 +1,196 @@
+package cyral
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/cyralinc/terraform-provider-cyral/client"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+const (
+ // Schema keys
+ SidecarInstanceListKey = "instance_list"
+ MetadataKey = "metadata"
+ MonitoringKey = "monitoring"
+ VersionKey = "version"
+ DynamicVersionKey = "dynamic_version"
+ CapabilitiesKey = "capabilities"
+ StartTimestampKey = "start_timestamp"
+ LastRegistrationKey = "last_registration"
+ RecyclingKey = "recycling"
+ RecyclableKey = "recyclable"
+ ServicesKey = "services"
+ MetricsPortKey = "metrics_port"
+ ComponentsKey = "components"
+ ErrorKey = "error"
+)
+
+func dataSourceSidecarInstance() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieve sidecar instances.",
+ ReadContext: ReadResource(ResourceOperationConfig{
+ Name: "SidecarInstanceDataSourceRead",
+ HttpMethod: http.MethodGet,
+ CreateURL: func(d *schema.ResourceData, c *client.Client) string {
+ return fmt.Sprintf(
+ "https://%s/v2/sidecars/%s/instances",
+ c.ControlPlane, d.Get(SidecarIDKey),
+ )
+ },
+ NewResponseData: func(_ *schema.ResourceData) ResponseData {
+ return &SidecarInstances{}
+ },
+ }),
+ Schema: map[string]*schema.Schema{
+ SidecarIDKey: {
+ Description: "Sidecar identifier.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ IDKey: {
+ Description: "Data source identifier.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ SidecarInstanceListKey: {
+ Description: "List of existing sidecar instances.",
+ Computed: true,
+ Type: schema.TypeList,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ IDKey: {
+ Description: "Instance identifier. Varies according to the computing platform that " +
+ "the sidecar is deployed to.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ MetadataKey: {
+ Description: "Instance metadata.",
+ Type: schema.TypeSet,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ VersionKey: {
+ Description: "Sidecar version that the instance is using.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ DynamicVersionKey: {
+ Description: "If true, indicates that the instance has dynamic versioning, " +
+ "that means that the version is not fixed at template level and it can be " +
+ "automatically upgraded.",
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+ CapabilitiesKey: {
+ Description: "Set of capabilities that can be enabled or disabled. **Note**: This " +
+ "field is per-instance, not per-sidecar, because not all sidecar instances might be " +
+ "in sync at some point in time.",
+ Type: schema.TypeSet,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ RecyclableKey: {
+ Description: "Indicates if sidecar instance will be recycled (e.g., by an ASG) " +
+ "if it reports itself as unhealthy.",
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+ },
+ },
+ },
+ StartTimestampKey: {
+ Description: "The time when the instance started.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ LastRegistrationKey: {
+ Description: "The last time the instance reported to the Control Plane.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ RecyclingKey: {
+ Description: "Indicates whether the Control Plane has asked the instance to mark " +
+ "itself unhealthy so that it is recycled by the infrastructure.",
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+ },
+ },
+ },
+ MonitoringKey: {
+ Description: "Instance monitoring information, such as its overall health.",
+ Type: schema.TypeSet,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ StatusKey: {
+ Description: "Aggregated status of all the sidecar services.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ ServicesKey: {
+ Description: "Sidecar instance services monitoring information.",
+ Type: schema.TypeMap,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeSet,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ StatusKey: {
+ Description: "Aggregated status of sidecar service.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ MetricsPortKey: {
+ Description: "Metrics port for service monitoring.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ ComponentsKey: {
+ Description: "Map of name to monitoring component. A component is a " +
+ "monitored check on the service that has its own status.",
+ Type: schema.TypeMap,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeSet,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ StatusKey: {
+ Description: "Component status.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ DescriptionKey: {
+ Description: "Describes what the type of check the component represents.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ ErrorKey: {
+ Description: "Error that describes what caused the current status.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ HostKey: {
+ Description: "Service host on the deployment.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}
diff --git a/cyral/data_source_cyral_sidecar_instance_ids.go b/cyral/data_source_cyral_sidecar_instance_ids.go
index 965511d5..81d69b7d 100644
--- a/cyral/data_source_cyral_sidecar_instance_ids.go
+++ b/cyral/data_source_cyral_sidecar_instance_ids.go
@@ -15,15 +15,17 @@ import (
)
type SidecarDetails struct {
- Instances []SidecarInstance `json:"instances,omitempty"`
+ Instances []DeprecatedSidecarInstances `json:"instances,omitempty"`
}
-type SidecarInstance struct {
+type DeprecatedSidecarInstances struct {
ASGInstanceID string `json:"asg_instance,omitempty"`
}
func dataSourceSidecarInstanceIDs() *schema.Resource {
return &schema.Resource{
+ DeprecationMessage: "This data source was deprecated. It will be removed in the next major version of " +
+ "the provider. Use the data source `cyral_sidecar_instance` instead",
Description: "Retrieves the IDs of all the current instances of a given sidecar.",
ReadContext: dataSourceSidecarInstanceIDsRead,
Schema: map[string]*schema.Schema{
diff --git a/cyral/data_source_cyral_sidecar_instance_test.go b/cyral/data_source_cyral_sidecar_instance_test.go
new file mode 100644
index 00000000..e91dca4f
--- /dev/null
+++ b/cyral/data_source_cyral_sidecar_instance_test.go
@@ -0,0 +1,88 @@
+package cyral
+
+import (
+ "fmt"
+ "regexp"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+)
+
+const (
+ sidecarInstanceDataSourceFullNameFmt = "data.cyral_sidecar_instance.%s"
+)
+
+func TestAccSidecarInstanceDataSource(t *testing.T) {
+ dataSourceName := "instances"
+ testSteps := []resource.TestStep{
+ accTestStepSidecarInstanceDataSource_EmptySidecarID(dataSourceName),
+ accTestStepSidecarInstanceDataSource_NoSidecarFoundForGivenID(dataSourceName),
+ accTestStepSidecarInstanceDataSource_NoSidecarInstances(dataSourceName),
+ }
+ resource.ParallelTest(
+ t, resource.TestCase{
+ ProviderFactories: providerFactories,
+ Steps: testSteps,
+ },
+ )
+}
+
+func accTestStepSidecarInstanceDataSource_EmptySidecarID(dataSourceName string) resource.TestStep {
+ config := fmt.Sprintf(`
+ data "cyral_sidecar_instance" "%s" {
+ }
+ `, dataSourceName)
+ return resource.TestStep{
+ Config: config,
+ ExpectError: regexp.MustCompile(fmt.Sprintf(`The argument "%s" is required`, SidecarIDKey)),
+ }
+}
+
+func accTestStepSidecarInstanceDataSource_NoSidecarFoundForGivenID(dataSourceName string) resource.TestStep {
+ nonExistentSidecarID := "some-non-existent-sidecar-id"
+ config := fmt.Sprintf(`
+ data "cyral_sidecar_instance" "%s" {
+ sidecar_id = %q
+ }
+ `, dataSourceName, nonExistentSidecarID)
+ return resource.TestStep{
+ Config: config,
+ ExpectError: regexp.MustCompile(fmt.Sprintf("sidecar with id '%s' does not exist", nonExistentSidecarID)),
+ }
+}
+
+func accTestStepSidecarInstanceDataSource_NoSidecarInstances(dataSourceName string) resource.TestStep {
+ // Creates a sidecar that doesn't have any instances, since it was not
+ // deployed.
+ config := formatBasicSidecarIntoConfig(
+ basicSidecarResName,
+ accTestName("data-sidecar-instance", "sidecar"),
+ "cft-ec2",
+ "",
+ )
+ config += fmt.Sprintf(`
+ data "cyral_sidecar_instance" "%s" {
+ sidecar_id = %s
+ }
+ `, dataSourceName, basicSidecarID)
+ dataSourceFullName := fmt.Sprintf(sidecarInstanceDataSourceFullNameFmt, dataSourceName)
+ check := resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrSet(
+ dataSourceFullName,
+ SidecarIDKey,
+ ),
+ resource.TestCheckResourceAttrSet(
+ dataSourceFullName,
+ IDKey,
+ ),
+ resource.TestCheckResourceAttr(
+ dataSourceFullName,
+ fmt.Sprintf("%s.#", SidecarInstanceListKey),
+ "0",
+ ),
+ )
+ return resource.TestStep{
+ Config: config,
+ Check: check,
+ }
+}
diff --git a/cyral/model_sidecar_instance.go b/cyral/model_sidecar_instance.go
new file mode 100644
index 00000000..c7597860
--- /dev/null
+++ b/cyral/model_sidecar_instance.go
@@ -0,0 +1,133 @@
+package cyral
+
+import (
+ "github.com/google/uuid"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+type SidecarInstances struct {
+ Instances []SidecarInstance `json:"instances"`
+}
+
+func (wrapper *SidecarInstances) WriteToSchema(d *schema.ResourceData) error {
+ d.SetId(uuid.New().String())
+ d.Set(SidecarInstanceListKey, wrapper.InstancesToInterfaceList())
+ return nil
+}
+
+func (wrapper *SidecarInstances) InstancesToInterfaceList() []any {
+ instancesInterfaceList := make([]any, len(wrapper.Instances))
+ for index, instance := range wrapper.Instances {
+ instancesInterfaceList[index] = instance.ToMap()
+ }
+ return instancesInterfaceList
+}
+
+type SidecarInstance struct {
+ ID string `json:"id"`
+ Metadata SidecarInstanceMetadata `json:"metadata"`
+ Monitoring SidecarInstanceMonitoring `json:"monitoring"`
+}
+
+func (instance *SidecarInstance) ToMap() map[string]any {
+ return map[string]any{
+ IDKey: instance.ID,
+ MetadataKey: instance.Metadata.ToInterfaceList(),
+ MonitoringKey: instance.Monitoring.ToInterfaceList(),
+ }
+}
+
+type SidecarInstanceMetadata struct {
+ Version string `json:"version"`
+ IsDynamicVersion bool `json:"isDynamicVersion"`
+ SidecarCapabilities *SidecarCapabilities `json:"capabilities"`
+ StartTimestamp string `json:"startTimestamp"`
+ LastRegistration string `json:"lastRegistration"`
+ IsRecycling bool `json:"isRecycling"`
+}
+
+func (metadata *SidecarInstanceMetadata) ToInterfaceList() []any {
+ return []any{
+ map[string]any{
+ VersionKey: metadata.Version,
+ DynamicVersionKey: metadata.IsDynamicVersion,
+ CapabilitiesKey: metadata.SidecarCapabilities.ToInterfaceList(),
+ StartTimestampKey: metadata.StartTimestamp,
+ LastRegistrationKey: metadata.LastRegistration,
+ RecyclingKey: metadata.IsRecycling,
+ },
+ }
+}
+
+type SidecarCapabilities struct {
+ Recyclable bool `json:"recyclable"`
+}
+
+func (capabilities *SidecarCapabilities) ToInterfaceList() []any {
+ if capabilities == nil {
+ return nil
+ }
+ return []any{
+ map[string]any{
+ RecyclableKey: capabilities.Recyclable,
+ },
+ }
+}
+
+type SidecarInstanceMonitoring struct {
+ Status string `json:"status"`
+ Services map[string]SidecarService `json:"services"`
+}
+
+func (monitoring *SidecarInstanceMonitoring) ToInterfaceList() []any {
+ var services map[string]any
+ if monitoring.Services != nil {
+ services = make(map[string]any, len(monitoring.Services))
+ }
+ for serviceKey, service := range monitoring.Services {
+ services[serviceKey] = service.ToMap()
+ }
+ return []any{
+ map[string]any{
+ StatusKey: monitoring.Status,
+ ServicesKey: services,
+ },
+ }
+}
+
+type SidecarService struct {
+ Status string `json:"status"`
+ MetricsPort uint32 `json:"metricsPort"`
+ Components map[string]SidecarServiceComponent `json:"components"`
+ Host string `json:"host"`
+}
+
+func (service *SidecarService) ToMap() map[string]any {
+ var components map[string]any
+ if service.Components != nil {
+ components = make(map[string]any, len(service.Components))
+ }
+ for componentKey, component := range service.Components {
+ components[componentKey] = component.ToMap()
+ }
+ return map[string]any{
+ StatusKey: service.Status,
+ MetricsPortKey: service.MetricsPort,
+ ComponentsKey: components,
+ HostKey: service.Host,
+ }
+}
+
+type SidecarServiceComponent struct {
+ Status string `json:"status"`
+ Description string `json:"description"`
+ Error string `json:"error"`
+}
+
+func (component *SidecarServiceComponent) ToMap() map[string]any {
+ return map[string]any{
+ StatusKey: component.Status,
+ DescriptionKey: component.Description,
+ ErrorKey: component.Error,
+ }
+}
diff --git a/cyral/provider.go b/cyral/provider.go
index 60050fc6..2b97b3b8 100644
--- a/cyral/provider.go
+++ b/cyral/provider.go
@@ -85,6 +85,7 @@ func Provider() *schema.Provider {
"cyral_sidecar_health": dataSourceSidecarHealth(),
"cyral_sidecar_id": dataSourceSidecarID(),
"cyral_sidecar_instance_ids": dataSourceSidecarInstanceIDs(),
+ "cyral_sidecar_instance": dataSourceSidecarInstance(),
"cyral_sidecar_listener": dataSourceSidecarListener(),
"cyral_system_info": dataSourceSystemInfo(),
},
diff --git a/docs/data-sources/sidecar_instance.md b/docs/data-sources/sidecar_instance.md
new file mode 100644
index 00000000..b803d877
--- /dev/null
+++ b/docs/data-sources/sidecar_instance.md
@@ -0,0 +1,75 @@
+# cyral_sidecar_instance (Data Source)
+
+Retrieve sidecar instances.
+
+## Schema
+
+### Required
+
+- `sidecar_id` (String) Sidecar identifier.
+
+### Read-Only
+
+- `id` (String) Data source identifier.
+- `instance_list` (List of Object) List of existing sidecar instances. (see [below for nested schema](#nestedatt--instance_list))
+
+
+
+### Nested Schema for `instance_list`
+
+Read-Only:
+
+- `id` (String) Instance identifier. Varies according to the computing platform that the sidecar is deployed to.
+- `metadata` (Set of Object) Instance metadata. (see [below for nested schema](#nestedatt--instance_list--metadata))
+- `monitoring` (Set of Object) Instance monitoring information, such as its overall health. (see [below for nested schema](#nestedatt--instance_list--monitoring))
+
+
+
+### Nested Schema for `instance_list.metadata`
+
+Read-Only:
+
+- `capabilities` (Set of Object) Set of capabilities that can be enabled or disabled. **Note**: This field is per-instance, not per-sidecar, because not all sidecar instances might be in sync at some point in time. (see [below for nested schema](#nestedatt--instance_list--metadata--capabilities))
+- `dynamic_version` (Boolean) If true, indicates that the instance has dynamic versioning, that means that the version is not fixed at template level and it can be automatically upgraded.
+- `last_registration` (String) The last time the instance reported to the Control Plane.
+- `recycling` (Boolean) Indicates whether the Control Plane has asked the instance to mark itself unhealthy so that it is recycled by the infrastructure.
+- `start_timestamp` (String) The time when the instance started.
+- `version` (String) Sidecar version that the instance is using.
+
+
+
+### Nested Schema for `instance_list.metadata.capabilities`
+
+Read-Only:
+
+- `recyclable` (Boolean) Indicates if sidecar instance will be recycled (e.g., by an ASG) if it reports itself as unhealthy.
+
+
+
+### Nested Schema for `instance_list.monitoring`
+
+Read-Only:
+
+- `services` (Map of Set of Object) Sidecar instance services monitoring information. (see [below for nested schema](#nestedatt--instance_list--monitoring--services))
+- `status` (String) Aggregated status of all the sidecar services.
+
+
+
+### Nested Schema for `instance_list.monitoring.services`
+
+Read-Only:
+
+- `status` (String) Aggregated status of sidecar service.
+- `metrics_port` (Number) Metrics port for service monitoring.
+- `components` (Map of Set of Object) Map of name to monitoring component. A component is a monitored check on the service that has its own status. (see [below for nested schema](#nestedatt--instance_list--monitoring--services--components))
+- `host` (String) Service host on the deployment.
+
+
+
+### Nested Schema for `instance_list.monitoring.services.components`
+
+Read-Only:
+
+- `status` (String) Component status.
+- `description` (String) Describes what the type of check the component represents.
+- `error` (String) Error that describes what caused the current status.
diff --git a/docs/data-sources/sidecar_instance_ids.md b/docs/data-sources/sidecar_instance_ids.md
index ab94d334..4e831322 100644
--- a/docs/data-sources/sidecar_instance_ids.md
+++ b/docs/data-sources/sidecar_instance_ids.md
@@ -3,12 +3,12 @@
page_title: "cyral_sidecar_instance_ids Data Source - terraform-provider-cyral"
subcategory: ""
description: |-
- Retrieves the IDs of all the current instances of a given sidecar.
+ ~> DEPRECATED This data source was deprecated. It will be removed in the next major version of the provider. Use the data source cyral_sidecar_instance instead
---
# cyral_sidecar_instance_ids (Data Source)
-Retrieves the IDs of all the current instances of a given sidecar.
+~> **DEPRECATED** This data source was deprecated. It will be removed in the next major version of the provider. Use the data source `cyral_sidecar_instance` instead
## Example Usage
diff --git a/templates/data-sources/sidecar_instance.md.tmpl b/templates/data-sources/sidecar_instance.md.tmpl
new file mode 100644
index 00000000..9d788962
--- /dev/null
+++ b/templates/data-sources/sidecar_instance.md.tmpl
@@ -0,0 +1,75 @@
+# {{ .Name | trimspace }} ({{ .Type | trimspace }})
+
+{{ .Description | trimspace }}
+
+## Schema
+
+### Required
+
+- `sidecar_id` (String) Sidecar identifier.
+
+### Read-Only
+
+- `id` (String) Data source identifier.
+- `instance_list` (List of Object) List of existing sidecar instances. (see [below for nested schema](#nestedatt--instance_list))
+
+
+
+### Nested Schema for `instance_list`
+
+Read-Only:
+
+- `id` (String) Instance identifier. Varies according to the computing platform that the sidecar is deployed to.
+- `metadata` (Set of Object) Instance metadata. (see [below for nested schema](#nestedatt--instance_list--metadata))
+- `monitoring` (Set of Object) Instance monitoring information, such as its overall health. (see [below for nested schema](#nestedatt--instance_list--monitoring))
+
+
+
+### Nested Schema for `instance_list.metadata`
+
+Read-Only:
+
+- `capabilities` (Set of Object) Set of capabilities that can be enabled or disabled. **Note**: This field is per-instance, not per-sidecar, because not all sidecar instances might be in sync at some point in time. (see [below for nested schema](#nestedatt--instance_list--metadata--capabilities))
+- `dynamic_version` (Boolean) If true, indicates that the instance has dynamic versioning, that means that the version is not fixed at template level and it can be automatically upgraded.
+- `last_registration` (String) The last time the instance reported to the Control Plane.
+- `recycling` (Boolean) Indicates whether the Control Plane has asked the instance to mark itself unhealthy so that it is recycled by the infrastructure.
+- `start_timestamp` (String) The time when the instance started.
+- `version` (String) Sidecar version that the instance is using.
+
+
+
+### Nested Schema for `instance_list.metadata.capabilities`
+
+Read-Only:
+
+- `recyclable` (Boolean) Indicates if sidecar instance will be recycled (e.g., by an ASG) if it reports itself as unhealthy.
+
+
+
+### Nested Schema for `instance_list.monitoring`
+
+Read-Only:
+
+- `services` (Map of Set of Object) Sidecar instance services monitoring information. (see [below for nested schema](#nestedatt--instance_list--monitoring--services))
+- `status` (String) Aggregated status of all the sidecar services.
+
+
+
+### Nested Schema for `instance_list.monitoring.services`
+
+Read-Only:
+
+- `status` (String) Aggregated status of sidecar service.
+- `metrics_port` (Number) Metrics port for service monitoring.
+- `components` (Map of Set of Object) Map of name to monitoring component. A component is a monitored check on the service that has its own status. (see [below for nested schema](#nestedatt--instance_list--monitoring--services--components))
+- `host` (String) Service host on the deployment.
+
+
+
+### Nested Schema for `instance_list.monitoring.services.components`
+
+Read-Only:
+
+- `status` (String) Component status.
+- `description` (String) Describes what the type of check the component represents.
+- `error` (String) Error that describes what caused the current status.