Skip to content

Commit

Permalink
feat: Generate component IDs before applying new resources (#142)
Browse files Browse the repository at this point in the history
* Generate component ID

* Test component func

* test id change

* add license

* lint: package comment
  • Loading branch information
jsirianni authored Feb 10, 2025
1 parent d64b7c8 commit 2b770fe
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 18 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0
github.com/observiq/bindplane-op-enterprise v1.86.0
github.com/oklog/ulid/v2 v2.1.0
github.com/stretchr/testify v1.10.0
github.com/testcontainers/testcontainers-go v0.35.0
go.uber.org/zap v1.27.0
Expand Down Expand Up @@ -135,7 +136,6 @@ require (
github.com/nats-io/nuid v1.0.1 // indirect
github.com/observiq/stanza v1.6.1 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.118.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.118.0 // indirect
Expand Down
30 changes: 30 additions & 0 deletions internal/component/component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright observIQ, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package component provides functions for defining bindplane
// component values.
package component

import (
"fmt"

"github.com/observiq/bindplane-op-enterprise/model"
)

// NewResourceID wraps model.NewResourceID and returns
// a new resource ID with the `tf` prefix to indicate
// that it was created by the Terraform provider.
func NewResourceID() string {
return fmt.Sprintf("tf-%s", model.NewResourceID())
}
30 changes: 30 additions & 0 deletions internal/component/component_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright observIQ, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package component

import (
"strings"
"testing"

"github.com/oklog/ulid/v2"
"github.com/stretchr/testify/require"
)

func TestNewResourceID(t *testing.T) {
id := NewResourceID()
require.True(t, strings.HasPrefix(id, "tf-"))
_, err := ulid.Parse(strings.TrimPrefix(id, "tf-"))
require.NoError(t, err)
}
3 changes: 2 additions & 1 deletion internal/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
// configurations, use AnyResourceFromConfigurationV1.
//
// rParameters and rProcessors can be nil.
func AnyResourceV1(rName, rType string, rKind model.Kind, rParameters []model.Parameter, rProcessors []model.ResourceConfiguration) (model.AnyResource, error) {
func AnyResourceV1(id, rName, rType string, rKind model.Kind, rParameters []model.Parameter, rProcessors []model.ResourceConfiguration) (model.AnyResource, error) {
procs := []map[string]string{}
for _, p := range rProcessors {
proc := map[string]string{}
Expand All @@ -47,6 +47,7 @@ func AnyResourceV1(rName, rType string, rKind model.Kind, rParameters []model.Pa
APIVersion: "bindplane.observiq.com/v1",
Kind: rKind,
Metadata: model.Metadata{
ID: id,
Name: rName,
},
},
Expand Down
9 changes: 8 additions & 1 deletion internal/resource/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
func TestAnyResourceV1(t *testing.T) {
cases := []struct {
name string
id string
rName string
rType string
rkind model.Kind
Expand All @@ -33,6 +34,7 @@ func TestAnyResourceV1(t *testing.T) {
}{
{
"source",
"tf-source",
"my-host",
"host",
model.KindSource,
Expand All @@ -51,6 +53,7 @@ func TestAnyResourceV1(t *testing.T) {
},
{
"destination",
"tf-destination",
"my-destination",
"googlecloud",
model.KindDestination,
Expand All @@ -65,6 +68,7 @@ func TestAnyResourceV1(t *testing.T) {
},
{
"processor",
"tf-processor",
"my-filter",
"filter",
model.KindProcessor,
Expand All @@ -74,6 +78,7 @@ func TestAnyResourceV1(t *testing.T) {
},
{
"extension",
"tf-extension",
"my-extension",
"pprof",
model.KindExtension,
Expand All @@ -83,6 +88,7 @@ func TestAnyResourceV1(t *testing.T) {
},
{
"invalid-kind",
"tf-resource",
"my-resource",
"resource",
model.KindAgent,
Expand All @@ -92,6 +98,7 @@ func TestAnyResourceV1(t *testing.T) {
},
{
"valid-processors",
"tf-bundle",
"my-bundle",
"bundle",
model.KindProcessor,
Expand All @@ -110,7 +117,7 @@ func TestAnyResourceV1(t *testing.T) {

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
_, err := AnyResourceV1(tc.rName, tc.rType, tc.rkind, tc.rParameters, tc.rProcessors)
_, err := AnyResourceV1(tc.id, tc.rName, tc.rType, tc.rkind, tc.rParameters, tc.rProcessors)
if tc.expectErr != "" {
require.Error(t, err)
require.ErrorContains(t, err, tc.expectErr)
Expand Down
9 changes: 8 additions & 1 deletion provider/resource_destination.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/observiq/bindplane-op-enterprise/model"
"github.com/observiq/terraform-provider-bindplane/client"
"github.com/observiq/terraform-provider-bindplane/internal/component"
"github.com/observiq/terraform-provider-bindplane/internal/parameter"
"github.com/observiq/terraform-provider-bindplane/internal/resource"
)
Expand Down Expand Up @@ -87,8 +88,14 @@ func resourceDestinationCreate(d *schema.ResourceData, meta any) error {
if c != nil {
return fmt.Errorf("destination with name '%s' already exists with id '%s'", name, c.ID())
}

// If a source does not already exist with this name
// and an ID is not set, generate and ID.
d.SetId(component.NewResourceID())
}

id := d.Id()

parameters := []model.Parameter{}
if s := d.Get("parameters_json").(string); s != "" {
params, err := parameter.StringToParameter(s)
Expand All @@ -98,7 +105,7 @@ func resourceDestinationCreate(d *schema.ResourceData, meta any) error {
parameters = params
}

r, err := resource.AnyResourceV1(name, destType, model.KindDestination, parameters, nil)
r, err := resource.AnyResourceV1(id, name, destType, model.KindDestination, parameters, nil)
if err != nil {
return err
}
Expand Down
9 changes: 8 additions & 1 deletion provider/resource_extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/observiq/bindplane-op-enterprise/model"
"github.com/observiq/terraform-provider-bindplane/client"
"github.com/observiq/terraform-provider-bindplane/internal/component"
"github.com/observiq/terraform-provider-bindplane/internal/parameter"
"github.com/observiq/terraform-provider-bindplane/internal/resource"
)
Expand Down Expand Up @@ -87,8 +88,14 @@ func resourceExtensionCreate(d *schema.ResourceData, meta any) error {
if c != nil {
return fmt.Errorf("extension with name '%s' already exists with id '%s'", name, c.ID())
}

// If a source does not already exist with this name
// and an ID is not set, generate and ID.
d.SetId(component.NewResourceID())
}

id := d.Id()

parameters := []model.Parameter{}
if s := d.Get("parameters_json").(string); s != "" {
params, err := parameter.StringToParameter(s)
Expand All @@ -98,7 +105,7 @@ func resourceExtensionCreate(d *schema.ResourceData, meta any) error {
parameters = params
}

r, err := resource.AnyResourceV1(name, extensionType, model.KindExtension, parameters, nil)
r, err := resource.AnyResourceV1(id, name, extensionType, model.KindExtension, parameters, nil)
if err != nil {
return err
}
Expand Down
14 changes: 4 additions & 10 deletions provider/resource_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,21 @@ func genericResourceRead(rKind model.Kind, d *schema.ResourceData, meta any) err
// A nil return from GenericResource indicates that the resource
// did not exist. Terraform read operations should always set the
// ID to "" and return a nil error. This will allow Terraform to
// re-create the resource or comfirm that it was deleted.
// re-create the resource or confirm that it was deleted.
if g == nil {
d.SetId("")
return nil
}

// Save values returned by bindplane to Terraform's state

d.SetId(g.ID)

// If the state ID is set but differs from the ID returned by,
// bindplane, mark the resource to be re-created by unsetting
// the ID. This will cause Terraform to attempt to create the resource
// instead of updating it. The creation step will fail because
// the resource already exists. This behavior is desirable, it will
// prevent Terraform from modifying resources created by other means.
if id := d.Id(); id != "" {
if g.ID != d.Id() {
d.SetId("")
return nil
}
if g.ID != d.Id() {
d.SetId("")
return nil
}

if err := d.Set("name", g.Name); err != nil {
Expand Down
9 changes: 8 additions & 1 deletion provider/resource_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/observiq/bindplane-op-enterprise/model"
"github.com/observiq/terraform-provider-bindplane/client"
"github.com/observiq/terraform-provider-bindplane/internal/component"
"github.com/observiq/terraform-provider-bindplane/internal/parameter"
"github.com/observiq/terraform-provider-bindplane/internal/resource"
)
Expand Down Expand Up @@ -87,8 +88,14 @@ func resourceProcessorCreate(d *schema.ResourceData, meta any) error {
if c != nil {
return fmt.Errorf("processor with name '%s' already exists with id '%s'", name, c.ID())
}

// If a source does not already exist with this name
// and an ID is not set, generate and ID.
d.SetId(component.NewResourceID())
}

id := d.Id()

parameters := []model.Parameter{}
if s := d.Get("parameters_json").(string); s != "" {
params, err := parameter.StringToParameter(s)
Expand All @@ -98,7 +105,7 @@ func resourceProcessorCreate(d *schema.ResourceData, meta any) error {
parameters = params
}

r, err := resource.AnyResourceV1(name, processorType, model.KindProcessor, parameters, nil)
r, err := resource.AnyResourceV1(id, name, processorType, model.KindProcessor, parameters, nil)
if err != nil {
return err
}
Expand Down
9 changes: 8 additions & 1 deletion provider/resource_processor_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/observiq/bindplane-op-enterprise/model"
"github.com/observiq/terraform-provider-bindplane/client"
"github.com/observiq/terraform-provider-bindplane/internal/component"
"github.com/observiq/terraform-provider-bindplane/internal/resource"
)

Expand Down Expand Up @@ -107,8 +108,14 @@ func resourceProcessorBundleCreate(d *schema.ResourceData, meta any) error {
if c != nil {
return fmt.Errorf("processor with name '%s' already exists with id '%s'", name, c.ID())
}

// If a source does not already exist with this name
// and an ID is not set, generate and ID.
d.SetId(component.NewResourceID())
}

id := d.Id()

// Using resource configuration instead of []string (names)
// to allow for future use of type + parameters_json.
processors := []model.ResourceConfiguration{}
Expand All @@ -131,7 +138,7 @@ func resourceProcessorBundleCreate(d *schema.ResourceData, meta any) error {
}
}

r, err := resource.AnyResourceV1(name, processorType, model.KindProcessor, nil, processors)
r, err := resource.AnyResourceV1(id, name, processorType, model.KindProcessor, nil, processors)
if err != nil {
return err
}
Expand Down
9 changes: 8 additions & 1 deletion provider/resource_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/observiq/bindplane-op-enterprise/model"
"github.com/observiq/terraform-provider-bindplane/client"
"github.com/observiq/terraform-provider-bindplane/internal/component"
"github.com/observiq/terraform-provider-bindplane/internal/parameter"
"github.com/observiq/terraform-provider-bindplane/internal/resource"
)
Expand Down Expand Up @@ -88,8 +89,14 @@ func resourceSourceCreate(d *schema.ResourceData, meta any) error {
if c != nil {
return fmt.Errorf("source with name '%s' already exists with id '%s'", name, c.ID())
}

// If a source does not already exist with this name
// and an ID is not set, generate and ID.
d.SetId(component.NewResourceID())
}

id := d.Id()

parameters := []model.Parameter{}
if s := d.Get("parameters_json").(string); s != "" {
params, err := parameter.StringToParameter(s)
Expand All @@ -99,7 +106,7 @@ func resourceSourceCreate(d *schema.ResourceData, meta any) error {
parameters = params
}

r, err := resource.AnyResourceV1(name, sourceType, model.KindSource, parameters, nil)
r, err := resource.AnyResourceV1(id, name, sourceType, model.KindSource, parameters, nil)
if err != nil {
return err
}
Expand Down

0 comments on commit 2b770fe

Please sign in to comment.