Skip to content

Commit

Permalink
Add V2 exported services config support (#399)
Browse files Browse the repository at this point in the history
* add v2 exported service config resources

* use api client raw methods

* update deps

* 1.18 bind type error fix

* edits
  • Loading branch information
skpratt committed Mar 21, 2024
1 parent ec9cd0a commit a982e05
Show file tree
Hide file tree
Showing 12 changed files with 749 additions and 69 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
consul-version: [1.16.2, 1.17.0-rc1]
consul-version: [1.16.6, 1.17.3, 1.18.0]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
Expand Down
133 changes: 133 additions & 0 deletions consul/data_source_consul_config_entry_v2_exported_services.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package consul

import (
"encoding/json"
"fmt"

pbmulticluster "github.com/hashicorp/consul/proto-public/pbmulticluster/v2"
"github.com/hashicorp/consul/proto-public/pbresource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"google.golang.org/protobuf/encoding/protojson"
)

func dataSourceConsulConfigEntryV2ExportedServices() *schema.Resource {
return &schema.Resource{
Read: dataSourceConsulV2ExportedServicesRead,

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "The name of the config entry to read.",
},

"kind": {
Type: schema.TypeString,
Required: true,
Description: "The kind of exported services config (ExportedServices, NamespaceExportedServices, PartitionExportedServices).",
},

"partition": {
Type: schema.TypeString,
Optional: true,
Description: "The partition the config entry is associated with.",
},

"namespace": {
Type: schema.TypeString,
Optional: true,
Description: "The namespace the config entry is associated with.",
},

"services": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
Description: "The exported services.",
},

"partition_consumers": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
Description: "The exported service partition consumers.",
},
"peer_consumers": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
Description: "The exported service peer consumers.",
},
"sameness_group_consumers": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
Description: "The exported service sameness group consumers.",
},
},
}
}

func dataSourceConsulV2ExportedServicesRead(d *schema.ResourceData, meta interface{}) error {
client, qOpts, _ := getClient(d, meta)
name := d.Get("name").(string)
kind := d.Get("kind").(string)
gvk := &GVK{
Group: pbmulticluster.GroupName,
Version: pbmulticluster.Version,
Kind: kind,
}
resp, err := v2MulticlusterRead(client, gvk, name, qOpts)
if err != nil || resp == nil || resp["id"] == nil || resp["data"] == nil {
return fmt.Errorf("exported services config not found: %s", name)
}
respData, err := json.Marshal(resp["data"])
if err != nil {
return fmt.Errorf("failed to unmarshal response data: %v", err)
}
data := &pbmulticluster.ExportedServices{}
if err = protojson.Unmarshal(respData, data); err != nil {
return fmt.Errorf("failed to unmarshal to proto message: %v", err)
}
respID, err := json.Marshal(resp["id"])
if err != nil {
return fmt.Errorf("failed to unmarshal response id: %v", err)
}
id := &pbresource.ID{}
if err = protojson.Unmarshal(respID, id); err != nil {
return fmt.Errorf("failed to unmarshal to proto message: %v", err)
}
var partitions []string
var peers []string
var samenessgroups []string
for _, e := range data.Consumers {
switch v := e.ConsumerTenancy.(type) {
case *pbmulticluster.ExportedServicesConsumer_Peer:
peers = append(peers, v.Peer)
case *pbmulticluster.ExportedServicesConsumer_Partition:
partitions = append(partitions, v.Partition)
case *pbmulticluster.ExportedServicesConsumer_SamenessGroup:
samenessgroups = append(samenessgroups, v.SamenessGroup)
default:
return fmt.Errorf("unknown exported service consumer type: %T", v)
}
}
d.SetId(id.Uid)
sw := newStateWriter(d)
sw.set("services", data.Services)
sw.set("partition_consumers", partitions)
sw.set("peer_consumers", peers)
sw.set("sameness_group_consumers", samenessgroups)
return sw.error()
}
116 changes: 116 additions & 0 deletions consul/data_source_consul_config_entry_v2_exported_services_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package consul

import (
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

func TestAccDataExportedServicesV2_basic(t *testing.T) {
providers, client := startTestServer(t)

resource.Test(t, resource.TestCase{
Providers: providers,
PreCheck: func() { skipTestOnConsulCommunityEdition(t) },
Steps: []resource.TestStep{
{
Config: testAccDataSourceExportedServicesV2ConfigNotFound,
SkipFunc: skipIfConsulVersionLT(client, "1.18.0"),
ExpectError: regexp.MustCompile(`exported services config not found: not-found`),
},
{
Config: testAccDataSourceExportedServicesV2ConfigBasic,
SkipFunc: skipIfConsulVersionLT(client, "1.18.0"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "name", "test"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "kind", "ExportedServices"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "namespace", "default"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "partition", "default"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "services.0", "s1"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "sameness_group_consumers.0", "sg1"),
),
},
{
Config: testAccDataSourceNamespaceExportedServicesV2ConfigBasic,
SkipFunc: skipIfConsulVersionLT(client, "1.18.0"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "name", "test"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "kind", "NamespaceExportedServices"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "partition", "default"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "partition_consumers.0", "default"),
),
},
{
Config: testAccDataSourcePartitionExportedServicesV2ConfigBasic,
SkipFunc: skipIfConsulVersionLT(client, "1.18.0"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "name", "test"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "kind", "PartitionExportedServices"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "partition", "default"),
resource.TestCheckResourceAttr("data.consul_config_entry_v2_exported_services.read", "peer_consumers.0", "peer1"),
),
},
},
})
}

const testAccDataSourcePartitionExportedServicesV2ConfigBasic = `
resource "consul_config_entry_v2_exported_services" "test" {
name = "test"
kind = "PartitionExportedServices"
partition = "default"
peer_consumers = ["peer1"]
}
data "consul_config_entry_v2_exported_services" "read" {
name = consul_config_entry_v2_exported_services.test.name
kind = consul_config_entry_v2_exported_services.test.kind
namespace = consul_config_entry_v2_exported_services.test.namespace
partition = consul_config_entry_v2_exported_services.test.partition
}
`

const testAccDataSourceNamespaceExportedServicesV2ConfigBasic = `
resource "consul_config_entry_v2_exported_services" "test" {
name = "test"
kind = "NamespaceExportedServices"
namespace = "default"
partition = "default"
partition_consumers = ["default"]
}
data "consul_config_entry_v2_exported_services" "read" {
name = consul_config_entry_v2_exported_services.test.name
kind = consul_config_entry_v2_exported_services.test.kind
namespace = consul_config_entry_v2_exported_services.test.namespace
partition = consul_config_entry_v2_exported_services.test.partition
}
`

const testAccDataSourceExportedServicesV2ConfigBasic = `
resource "consul_config_entry_v2_exported_services" "test" {
name = "test"
kind = "ExportedServices"
namespace = "default"
partition = "default"
services = ["s1"]
sameness_group_consumers = ["sg1"]
}
data "consul_config_entry_v2_exported_services" "read" {
name = consul_config_entry_v2_exported_services.test.name
kind = consul_config_entry_v2_exported_services.test.kind
namespace = consul_config_entry_v2_exported_services.test.namespace
partition = consul_config_entry_v2_exported_services.test.partition
}
`

const testAccDataSourceExportedServicesV2ConfigNotFound = `
data "consul_config_entry_v2_exported_services" "test" {
name = "not-found"
kind = "ExportedServices"
namespace = "default"
partition = "default"
}
`
2 changes: 1 addition & 1 deletion consul/resource_consul_acl_binding_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func TestAccConsulACLBindingRule_basic(t *testing.T) {
},
{
Config: testResourceACLBindingRuleConfig_wrongType,
ExpectError: regexp.MustCompile(`Invalid Binding Rule: unknown BindType "foobar"`),
ExpectError: regexp.MustCompile(`unknown BindType "foobar"`),
},
},
})
Expand Down
Loading

0 comments on commit a982e05

Please sign in to comment.