diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 43ef980d..4bcfa122 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -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: diff --git a/consul/data_source_consul_config_entry_v2_exported_services.go b/consul/data_source_consul_config_entry_v2_exported_services.go new file mode 100644 index 00000000..12d4a180 --- /dev/null +++ b/consul/data_source_consul_config_entry_v2_exported_services.go @@ -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() +} diff --git a/consul/data_source_consul_config_entry_v2_exported_services_test.go b/consul/data_source_consul_config_entry_v2_exported_services_test.go new file mode 100644 index 00000000..dc011f0e --- /dev/null +++ b/consul/data_source_consul_config_entry_v2_exported_services_test.go @@ -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" +} +` diff --git a/consul/resource_consul_acl_binding_rule_test.go b/consul/resource_consul_acl_binding_rule_test.go index 9e207dbf..2a8e546b 100644 --- a/consul/resource_consul_acl_binding_rule_test.go +++ b/consul/resource_consul_acl_binding_rule_test.go @@ -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"`), }, }, }) diff --git a/consul/resource_consul_config_entry_v2_exported_services.go b/consul/resource_consul_config_entry_v2_exported_services.go new file mode 100644 index 00000000..2bb1bb19 --- /dev/null +++ b/consul/resource_consul_config_entry_v2_exported_services.go @@ -0,0 +1,203 @@ +// 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 resourceConsulV2ExportedServices() *schema.Resource { + return &schema.Resource{ + Create: resourceConsulV2ExportedServicesCreate, + Update: resourceConsulV2ExportedServicesUpdate, + Read: resourceConsulV2ExportedServicesRead, + Delete: resourceConsulV2ExportedServicesDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + 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, + Required: true, + Description: "The partition the config entry is associated with.", + ForceNew: true, + }, + + "namespace": { + Type: schema.TypeString, + Optional: true, + Description: "The namespace the config entry is associated with.", + ForceNew: true, + }, + + "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 resourceConsulV2ExportedServicesCreate(d *schema.ResourceData, meta interface{}) error { + return resourceConsulV2ExportedServicesUpdate(d, meta) +} + +func resourceConsulV2ExportedServicesUpdate(d *schema.ResourceData, meta interface{}) error { + client, _, wOpts := getClient(d, meta) + name := d.Get("name").(string) + kind := d.Get("kind").(string) + gvk := &GVK{ + Group: "multicluster", + Version: "v2", + Kind: kind, + } + var consumers []map[string]any + for _, p := range d.Get("peer_consumers").([]interface{}) { + consumers = append(consumers, map[string]any{"peer": p}) + } + for _, ap := range d.Get("partition_consumers").([]interface{}) { + consumers = append(consumers, map[string]any{"partition": ap}) + } + for _, sg := range d.Get("sameness_group_consumers").([]interface{}) { + consumers = append(consumers, map[string]any{"sameness_group": sg}) + } + data := map[string]any{"consumers": consumers} + services := d.Get("services").([]interface{}) + if len(services) > 0 { + data["services"] = services + } + wReq := &V2WriteRequest{ + Metadata: nil, + Data: data, + Owner: nil, + } + resp, _, err := v2MulticlusterApply(client, gvk, name, wOpts, wReq) + if err != nil || resp == nil { + return fmt.Errorf("failed to write exported services config '%s': %v", name, err) + } + d.SetId(resp.ID.Type.Kind + resp.ID.Tenancy.Partition + resp.ID.Tenancy.Namespace + resp.ID.Name) + sw := newStateWriter(d) + sw.set("name", resp.ID.Name) + sw.set("kind", resp.ID.Type.Kind) + sw.set("partition", resp.ID.Tenancy.Partition) + sw.set("namespace", resp.ID.Tenancy.Namespace) + return resourceConsulV2ExportedServicesRead(d, meta) +} + +func resourceConsulV2ExportedServicesRead(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 { + 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) + } + } + sw := newStateWriter(d) + sw.set("services", data.Services) + sw.set("name", id.Name) + sw.set("kind", id.Type.Kind) + sw.set("partition", id.Tenancy.Partition) + sw.set("namespace", id.Tenancy.Namespace) + sw.set("partition_consumers", partitions) + sw.set("peer_consumers", peers) + sw.set("sameness_group_consumers", samenessgroups) + return sw.error() +} + +func resourceConsulV2ExportedServicesDelete(d *schema.ResourceData, meta interface{}) error { + client, qOpts, _ := getClient(d, meta) + kind := d.Get("kind").(string) + gvk := &GVK{ + Group: pbmulticluster.GroupName, + Version: pbmulticluster.Version, + Kind: kind, + } + name := d.Get("name").(string) + return v2MulticlusterDelete(client, gvk, name, qOpts) +} diff --git a/consul/resource_consul_config_entry_v2_exported_services_test.go b/consul/resource_consul_config_entry_v2_exported_services_test.go new file mode 100644 index 00000000..083d8afb --- /dev/null +++ b/consul/resource_consul_config_entry_v2_exported_services_test.go @@ -0,0 +1,92 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package consul + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccConsulExportedServicesV2_basic(t *testing.T) { + providers, client := startTestServer(t) + + resource.Test(t, resource.TestCase{ + Providers: providers, + PreCheck: func() { skipTestOnConsulCommunityEdition(t) }, + Steps: []resource.TestStep{ + { + Config: testAccConsulExportedServicesV2Basic, + SkipFunc: skipIfConsulVersionLT(client, "1.18.0"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.test", "name", "test"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.test", "kind", "ExportedServices"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.test", "partition", "default"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.test", "services.#", "2"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.test", "services.0", "s1"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.test", "services.1", "s2"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.test", "sameness_group_consumers.#", "1"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.test", "sameness_group_consumers.0", "sg1"), + ), + }, + { + Config: testAccConsulNamespaceExportedServicesV2Basic, + SkipFunc: skipIfConsulVersionLT(client, "1.18.0"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.nstest", "name", "nstest"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.nstest", "kind", "NamespaceExportedServices"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.nstest", "partition", "default"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.nstest", "peer_consumers.#", "1"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.nstest", "peer_consumers.0", "p1"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.nstest", "partition_consumers.#", "2"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.nstest", "partition_consumers.0", "ap1"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.nstest", "partition_consumers.1", "ap2"), + ), + }, + { + Config: testAccConsulPartitionExportedServicesV2Basic, + SkipFunc: skipIfConsulVersionLT(client, "1.18.0"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.ptest", "name", "ptest"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.ptest", "kind", "PartitionExportedServices"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.ptest", "partition", "default"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.ptest", "peer_consumers.#", "1"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.ptest", "peer_consumers.0", "p1"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.ptest", "partition_consumers.#", "2"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.ptest", "partition_consumers.0", "ap1"), + resource.TestCheckResourceAttr("consul_config_entry_v2_exported_services.ptest", "partition_consumers.1", "ap2"), + ), + }, + }, + }) +} + +const testAccConsulExportedServicesV2Basic = ` +resource "consul_config_entry_v2_exported_services" "test" { + name = "test" + kind = "ExportedServices" + namespace = "default" + partition = "default" + services = ["s1", "s2"] + sameness_group_consumers = ["sg1"] +}` + +const testAccConsulNamespaceExportedServicesV2Basic = ` +resource "consul_config_entry_v2_exported_services" "nstest" { + name = "nstest" + kind = "NamespaceExportedServices" + namespace = "default" + partition = "default" + peer_consumers = ["p1"] + partition_consumers = ["ap1", "ap2"] +}` + +const testAccConsulPartitionExportedServicesV2Basic = ` +resource "consul_config_entry_v2_exported_services" "ptest" { + name = "ptest" + kind = "PartitionExportedServices" + partition = "default" + peer_consumers = ["p1"] + partition_consumers = ["ap1", "ap2"] +}` diff --git a/consul/resource_provider.go b/consul/resource_provider.go index 5b757876..41782415 100644 --- a/consul/resource_provider.go +++ b/consul/resource_provider.go @@ -196,26 +196,27 @@ func Provider() terraform.ResourceProvider { }, DataSourcesMap: map[string]*schema.Resource{ - "consul_agent_self": dataSourceConsulAgentSelf(), - "consul_agent_config": dataSourceConsulAgentConfig(), - "consul_autopilot_health": dataSourceConsulAutopilotHealth(), - "consul_nodes": dataSourceConsulNodes(), - "consul_service": dataSourceConsulService(), - "consul_service_health": dataSourceConsulServiceHealth(), - "consul_services": dataSourceConsulServices(), - "consul_keys": dataSourceConsulKeys(), - "consul_key_prefix": dataSourceConsulKeyPrefix(), - "consul_acl_auth_method": dataSourceConsulACLAuthMethod(), - "consul_acl_policy": dataSourceConsulACLPolicy(), - "consul_acl_role": dataSourceConsulACLRole(), - "consul_acl_token": dataSourceConsulACLToken(), - "consul_acl_token_secret_id": dataSourceConsulACLTokenSecretID(), - "consul_network_segments": dataSourceConsulNetworkSegments(), - "consul_network_area_members": dataSourceConsulNetworkAreaMembers(), - "consul_datacenters": dataSourceConsulDatacenters(), - "consul_config_entry": dataSourceConsulConfigEntry(), - "consul_peering": dataSourceConsulPeering(), - "consul_peerings": dataSourceConsulPeerings(), + "consul_agent_self": dataSourceConsulAgentSelf(), + "consul_agent_config": dataSourceConsulAgentConfig(), + "consul_autopilot_health": dataSourceConsulAutopilotHealth(), + "consul_nodes": dataSourceConsulNodes(), + "consul_service": dataSourceConsulService(), + "consul_service_health": dataSourceConsulServiceHealth(), + "consul_services": dataSourceConsulServices(), + "consul_keys": dataSourceConsulKeys(), + "consul_key_prefix": dataSourceConsulKeyPrefix(), + "consul_acl_auth_method": dataSourceConsulACLAuthMethod(), + "consul_acl_policy": dataSourceConsulACLPolicy(), + "consul_acl_role": dataSourceConsulACLRole(), + "consul_acl_token": dataSourceConsulACLToken(), + "consul_acl_token_secret_id": dataSourceConsulACLTokenSecretID(), + "consul_network_segments": dataSourceConsulNetworkSegments(), + "consul_network_area_members": dataSourceConsulNetworkAreaMembers(), + "consul_datacenters": dataSourceConsulDatacenters(), + "consul_config_entry": dataSourceConsulConfigEntry(), + "consul_config_entry_v2_exported_services": dataSourceConsulConfigEntryV2ExportedServices(), + "consul_peering": dataSourceConsulPeering(), + "consul_peerings": dataSourceConsulPeerings(), // Aliases to limit the impact of rename of catalog // datasources @@ -225,38 +226,39 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "consul_acl_auth_method": resourceConsulACLAuthMethod(), - "consul_acl_binding_rule": resourceConsulACLBindingRule(), - "consul_acl_policy": resourceConsulACLPolicy(), - "consul_acl_role_policy_attachment": resourceConsulACLRolePolicyAttachment(), - "consul_acl_role": resourceConsulACLRole(), - "consul_acl_token_policy_attachment": resourceConsulACLTokenPolicyAttachment(), - "consul_acl_token_role_attachment": resourceConsulACLTokenRoleAttachment(), - "consul_acl_token": resourceConsulACLToken(), - "consul_admin_partition": resourceConsulAdminPartition(), - "consul_agent_service": resourceConsulAgentService(), - "consul_autopilot_config": resourceConsulAutopilotConfig(), - "consul_catalog_entry": resourceConsulCatalogEntry(), - "consul_certificate_authority": resourceConsulCertificateAuthority(), - "consul_config_entry_service_defaults": resourceFromConfigEntryImplementation(&serviceDefaults{}), - "consul_config_entry_service_intentions": resourceFromConfigEntryImplementation(&serviceIntentions{}), - "consul_config_entry_service_resolver": resourceFromConfigEntryImplementation(&serviceResolver{}), - "consul_config_entry_service_router": resourceFromConfigEntryImplementation(&serviceRouter{}), - "consul_config_entry_service_splitter": resourceFromConfigEntryImplementation(&serviceSplitter{}), - "consul_config_entry": resourceConsulConfigEntry(), - "consul_intention": resourceConsulIntention(), - "consul_key_prefix": resourceConsulKeyPrefix(), - "consul_keys": resourceConsulKeys(), - "consul_license": resourceConsulLicense(), - "consul_namespace_policy_attachment": resourceConsulNamespacePolicyAttachment(), - "consul_namespace_role_attachment": resourceConsulNamespaceRoleAttachment(), - "consul_namespace": resourceConsulNamespace(), - "consul_network_area": resourceConsulNetworkArea(), - "consul_node": resourceConsulNode(), - "consul_peering_token": resourceSourceConsulPeeringToken(), - "consul_peering": resourceSourceConsulPeering(), - "consul_prepared_query": resourceConsulPreparedQuery(), - "consul_service": resourceConsulService(), + "consul_acl_auth_method": resourceConsulACLAuthMethod(), + "consul_acl_binding_rule": resourceConsulACLBindingRule(), + "consul_acl_policy": resourceConsulACLPolicy(), + "consul_acl_role_policy_attachment": resourceConsulACLRolePolicyAttachment(), + "consul_acl_role": resourceConsulACLRole(), + "consul_acl_token_policy_attachment": resourceConsulACLTokenPolicyAttachment(), + "consul_acl_token_role_attachment": resourceConsulACLTokenRoleAttachment(), + "consul_acl_token": resourceConsulACLToken(), + "consul_admin_partition": resourceConsulAdminPartition(), + "consul_agent_service": resourceConsulAgentService(), + "consul_autopilot_config": resourceConsulAutopilotConfig(), + "consul_catalog_entry": resourceConsulCatalogEntry(), + "consul_certificate_authority": resourceConsulCertificateAuthority(), + "consul_config_entry_v2_exported_services": resourceConsulV2ExportedServices(), + "consul_config_entry_service_defaults": resourceFromConfigEntryImplementation(&serviceDefaults{}), + "consul_config_entry_service_intentions": resourceFromConfigEntryImplementation(&serviceIntentions{}), + "consul_config_entry_service_resolver": resourceFromConfigEntryImplementation(&serviceResolver{}), + "consul_config_entry_service_router": resourceFromConfigEntryImplementation(&serviceRouter{}), + "consul_config_entry_service_splitter": resourceFromConfigEntryImplementation(&serviceSplitter{}), + "consul_config_entry": resourceConsulConfigEntry(), + "consul_intention": resourceConsulIntention(), + "consul_key_prefix": resourceConsulKeyPrefix(), + "consul_keys": resourceConsulKeys(), + "consul_license": resourceConsulLicense(), + "consul_namespace_policy_attachment": resourceConsulNamespacePolicyAttachment(), + "consul_namespace_role_attachment": resourceConsulNamespaceRoleAttachment(), + "consul_namespace": resourceConsulNamespace(), + "consul_network_area": resourceConsulNetworkArea(), + "consul_node": resourceConsulNode(), + "consul_peering_token": resourceSourceConsulPeeringToken(), + "consul_peering": resourceSourceConsulPeering(), + "consul_prepared_query": resourceConsulPreparedQuery(), + "consul_service": resourceConsulService(), }, ConfigureFunc: providerConfigure, diff --git a/consul/v2_resource_provider_helper.go b/consul/v2_resource_provider_helper.go new file mode 100644 index 00000000..342cba35 --- /dev/null +++ b/consul/v2_resource_provider_helper.go @@ -0,0 +1,63 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package consul + +import ( + "fmt" + "strings" + + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +type GVK struct { + Group string + Version string + Kind string +} + +type V2WriteRequest struct { + Metadata map[string]string `json:"metadata"` + Data map[string]any `json:"data"` + Owner *pbresource.ID `json:"owner"` +} + +type V2WriteResponse struct { + Metadata map[string]string `json:"metadata"` + Data map[string]any `json:"data"` + Owner *pbresource.ID `json:"owner,omitempty"` + ID *pbresource.ID `json:"id"` + Version string `json:"version"` + Generation string `json:"generation"` + Status map[string]any `json:"status"` +} + +func v2MulticlusterRead(client *api.Client, gvk *GVK, resourceName string, q *api.QueryOptions) (map[string]interface{}, error) { + endpoint := strings.ToLower(fmt.Sprintf("/api/%s/%s/%s/%s", gvk.Group, gvk.Version, gvk.Kind, resourceName)) + var out map[string]interface{} + _, err := client.Raw().Query(endpoint, &out, q) + if err != nil { + return nil, err + } + return out, nil +} + +func v2MulticlusterDelete(client *api.Client, gvk *GVK, resourceName string, q *api.QueryOptions) error { + endpoint := strings.ToLower(fmt.Sprintf("/api/%s/%s/%s/%s", gvk.Group, gvk.Version, gvk.Kind, resourceName)) + _, err := client.Raw().Delete(endpoint, q) + if err != nil { + return err + } + return nil +} + +func v2MulticlusterApply(client *api.Client, gvk *GVK, resourceName string, w *api.WriteOptions, payload *V2WriteRequest) (*V2WriteResponse, *api.WriteMeta, error) { + endpoint := strings.ToLower(fmt.Sprintf("/api/%s/%s/%s/%s", gvk.Group, gvk.Version, gvk.Kind, resourceName)) + out := &V2WriteResponse{} + wm, err := client.Raw().Write(endpoint, payload, out, w) + if err != nil { + return nil, nil, err + } + return out, wm, nil +} diff --git a/docs/data-sources/config_entry_v2_exported_services.md b/docs/data-sources/config_entry_v2_exported_services.md new file mode 100644 index 00000000..6b0e6665 --- /dev/null +++ b/docs/data-sources/config_entry_v2_exported_services.md @@ -0,0 +1,34 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "consul_config_entry_v2_exported_services Data Source - terraform-provider-consul" +subcategory: "" +description: |- + +--- + +# consul_config_entry_v2_exported_services (Data Source) + + + + + + +## Schema + +### Required + +- `kind` (String) The kind of exported services config (ExportedServices, NamespaceExportedServices, PartitionExportedServices). +- `name` (String) The name of the config entry to read. + +### Optional + +- `namespace` (String) The namespace the config entry is associated with. +- `partition` (String) The partition the config entry is associated with. +- `partition_consumers` (List of String) The exported service partition consumers. +- `peer_consumers` (List of String) The exported service peer consumers. +- `sameness_group_consumers` (List of String) The exported service sameness group consumers. +- `services` (List of String) The exported services. + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/docs/resources/config_entry_v2_exported_services.md b/docs/resources/config_entry_v2_exported_services.md new file mode 100644 index 00000000..5e65e799 --- /dev/null +++ b/docs/resources/config_entry_v2_exported_services.md @@ -0,0 +1,34 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "consul_config_entry_v2_exported_services Resource - terraform-provider-consul" +subcategory: "" +description: |- + +--- + +# consul_config_entry_v2_exported_services (Resource) + + + + + + +## Schema + +### Required + +- `kind` (String) The kind of exported services config (ExportedServices, NamespaceExportedServices, PartitionExportedServices). +- `name` (String) The name of the config entry to read. +- `partition` (String) The partition the config entry is associated with. + +### Optional + +- `namespace` (String) The namespace the config entry is associated with. +- `partition_consumers` (List of String) The exported service partition consumers. +- `peer_consumers` (List of String) The exported service peer consumers. +- `sameness_group_consumers` (List of String) The exported service sameness group consumers. +- `services` (List of String) The exported services. + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/go.mod b/go.mod index 3103335a..ab2ec304 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,8 @@ module github.com/hashicorp/terraform-provider-consul require ( - github.com/hashicorp/consul/api v1.26.1-rc1 + github.com/hashicorp/consul/api v1.10.1-0.20240305172350-4e7982a5b707 + github.com/hashicorp/consul/proto-public v0.6.0 github.com/hashicorp/errwrap v1.1.0 github.com/hashicorp/terraform-plugin-sdk v1.17.2 github.com/mitchellh/mapstructure v1.5.0 @@ -39,7 +40,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/gax-go/v2 v2.7.1 // indirect - github.com/hashicorp/consul/sdk v0.14.3-rc1 + github.com/hashicorp/consul/sdk v0.15.0 github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-getter v1.7.0 // indirect @@ -90,14 +91,14 @@ require ( golang.org/x/crypto v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.114.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.56.3 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.32.0 ) go 1.21 diff --git a/go.sum b/go.sum index 2f646dc9..f4176150 100644 --- a/go.sum +++ b/go.sum @@ -410,10 +410,12 @@ github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6c github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.26.1-rc1 h1:eO+53vwWxEV2TMbTVrJbXp2TJjJNv+Nxij8j+xi3yOc= -github.com/hashicorp/consul/api v1.26.1-rc1/go.mod h1:ZKnaWXL2r23i+SPmEj1H5YsRNUS/uUzB2uhmD2QY4IY= -github.com/hashicorp/consul/sdk v0.14.3-rc1 h1:kE0dLXXTnvm67PNRE527XpSwqi5vwWoP+VkNHdBBR7o= -github.com/hashicorp/consul/sdk v0.14.3-rc1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= +github.com/hashicorp/consul/api v1.10.1-0.20240305172350-4e7982a5b707 h1:MrRUoVSf9aqxENuxLggbawazAkaiHMMX4KZR8Q0lv/s= +github.com/hashicorp/consul/api v1.10.1-0.20240305172350-4e7982a5b707/go.mod h1:GmIks4tBN5K6CD5UG2yS5IHR/UryqfZ31YBqPh/pA8E= +github.com/hashicorp/consul/proto-public v0.6.0 h1:9qrBujmoTB5gQQ84kQO+YWvhjgYoYBNrOoHdo4cpHHM= +github.com/hashicorp/consul/proto-public v0.6.0/go.mod h1:JF6983XNCzvw4wDNOLEwLqOq2IPw7iyT+pkswHSz08U= +github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU= +github.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -925,8 +927,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -940,8 +942,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1226,8 +1228,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=