-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate integer resource to framework (#177)
- Loading branch information
1 parent
a3d9560
commit 110d377
Showing
6 changed files
with
240 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
package provider_fm | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/tfsdk" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
type resourceIntegerType struct{} | ||
|
||
func (r resourceIntegerType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { | ||
return tfsdk.Schema{ | ||
Description: "The resource `random_integer` generates random values from a given range, described " + | ||
"by the `min` and `max` attributes of a given resource.\n" + | ||
"\n" + | ||
"This resource can be used in conjunction with resources that have the `create_before_destroy` " + | ||
"lifecycle flag set, to avoid conflicts with unique names during the brief period where both the " + | ||
"old and new resources exist concurrently.", | ||
Attributes: map[string]tfsdk.Attribute{ | ||
"keepers": { | ||
Description: "Arbitrary map of values that, when changed, will trigger recreation of " + | ||
"resource. See [the main provider documentation](../index.html) for more information.", | ||
Type: types.MapType{ | ||
ElemType: types.StringType, | ||
}, | ||
Optional: true, | ||
PlanModifiers: []tfsdk.AttributePlanModifier{tfsdk.RequiresReplace()}, | ||
}, | ||
"min": { | ||
Description: "The minimum inclusive value of the range.", | ||
Type: types.Int64Type, | ||
Required: true, | ||
PlanModifiers: []tfsdk.AttributePlanModifier{tfsdk.RequiresReplace()}, | ||
}, | ||
"max": { | ||
Description: "The maximum inclusive value of the range.", | ||
Type: types.Int64Type, | ||
Required: true, | ||
PlanModifiers: []tfsdk.AttributePlanModifier{tfsdk.RequiresReplace()}, | ||
}, | ||
"seed": { | ||
Description: "A custom seed to always produce the same value.", | ||
Type: types.StringType, | ||
Optional: true, | ||
PlanModifiers: []tfsdk.AttributePlanModifier{tfsdk.RequiresReplace()}, | ||
}, | ||
"result": { | ||
Description: "The random integer result.", | ||
Type: types.Int64Type, | ||
Computed: true, | ||
}, | ||
"id": { | ||
Description: "The generated uuid presented in string format.", | ||
Type: types.StringType, | ||
Computed: true, | ||
}, | ||
}, | ||
}, nil | ||
} | ||
|
||
func (r resourceIntegerType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) { | ||
return resourceInteger{ | ||
p: *(p.(*provider)), | ||
}, nil | ||
} | ||
|
||
type resourceInteger struct { | ||
p provider | ||
} | ||
|
||
func (r resourceInteger) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { | ||
if !r.p.configured { | ||
resp.Diagnostics.AddError( | ||
"provider not configured", | ||
"provider not configured", | ||
) | ||
} | ||
|
||
var plan Integer | ||
diags := req.Plan.Get(ctx, &plan) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
max := int(plan.Max.Value) | ||
min := int(plan.Min.Value) | ||
seed := plan.Seed.Value | ||
|
||
if max < min { | ||
resp.Diagnostics.AddError( | ||
"minimum value needs to be smaller than or equal to maximum value", | ||
"minimum value needs to be smaller than or equal to maximum value", | ||
) | ||
return | ||
} | ||
|
||
rand := NewRand(seed) | ||
number := rand.Intn((max+1)-min) + min | ||
|
||
u := &Integer{ | ||
ID: types.String{Value: strconv.Itoa(number)}, | ||
Keepers: plan.Keepers, | ||
Min: types.Int64{Value: int64(min)}, | ||
Max: types.Int64{Value: int64(max)}, | ||
Result: types.Int64{Value: int64(number)}, | ||
} | ||
|
||
if seed != "" { | ||
u.Seed.Value = seed | ||
} else { | ||
u.Seed.Null = true | ||
} | ||
|
||
diags = resp.State.Set(ctx, u) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
} | ||
|
||
func (r resourceInteger) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) { | ||
// Intentionally left blank. | ||
} | ||
|
||
func (r resourceInteger) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) { | ||
// Intentionally left blank. | ||
} | ||
|
||
func (r resourceInteger) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) { | ||
resp.State.RemoveResource(ctx) | ||
} | ||
|
||
func (r resourceInteger) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { | ||
parts := strings.Split(req.ID, ",") | ||
if len(parts) != 3 && len(parts) != 4 { | ||
resp.Diagnostics.AddError( | ||
"Invalid import usage: expecting {result},{min},{max} or {result},{min},{max},{seed}", | ||
"Invalid import usage: expecting {result},{min},{max} or {result},{min},{max},{seed}", | ||
) | ||
return | ||
} | ||
|
||
result, err := strconv.ParseInt(parts[0], 10, 64) | ||
if err != nil { | ||
resp.Diagnostics.AddError( | ||
"error parsing result", | ||
fmt.Sprintf("error parsing result: %s", err), | ||
) | ||
return | ||
} | ||
|
||
min, err := strconv.ParseInt(parts[1], 10, 64) | ||
if err != nil { | ||
resp.Diagnostics.AddError( | ||
"error parsing min", | ||
fmt.Sprintf("error parsing min: %s", err), | ||
) | ||
return | ||
} | ||
|
||
max, err := strconv.ParseInt(parts[2], 10, 64) | ||
if err != nil { | ||
resp.Diagnostics.AddError( | ||
"error parsing max", | ||
fmt.Sprintf("error parsing max: %s", err), | ||
) | ||
return | ||
} | ||
|
||
var state Integer | ||
|
||
state.ID.Value = parts[0] | ||
state.Keepers.ElemType = types.StringType | ||
state.Result.Value = int64(result) | ||
state.Min.Value = int64(min) | ||
state.Max.Value = int64(max) | ||
|
||
if len(parts) == 4 { | ||
state.Seed.Value = parts[3] | ||
} | ||
|
||
diags := resp.State.Set(ctx, &state) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package provider_fm | ||
|
||
import ( | ||
"hash/crc64" | ||
"math/rand" | ||
"time" | ||
) | ||
|
||
// NewRand returns a seeded random number generator, using a seed derived | ||
// from the provided string. | ||
// | ||
// If the seed string is empty, the current time is used as a seed. | ||
func NewRand(seed string) *rand.Rand { | ||
var seedInt int64 | ||
if seed != "" { | ||
crcTable := crc64.MakeTable(crc64.ISO) | ||
seedInt = int64(crc64.Checksum([]byte(seed), crcTable)) | ||
} else { | ||
seedInt = time.Now().UnixNano() | ||
} | ||
|
||
randSource := rand.NewSource(seedInt) | ||
return rand.New(randSource) | ||
} |