1
0
Fork 0
mirror of https://github.com/imjasonh/terraform-provider-melange synced 2026-07-08 02:25:13 +00:00

test resource update

Signed-off-by: Jason Hall <jason@chainguard.dev>
This commit is contained in:
Jason Hall 2023-09-30 21:20:02 -04:00
parent d2ad8d1f22
commit 1dad0d8f10
Failed to extract signature
2 changed files with 96 additions and 24 deletions

View file

@ -92,17 +92,19 @@ func (r *BuildResource) Create(ctx context.Context, req resource.CreateRequest,
return
}
id, err := r.doBuild(ctx, data)
if err != nil {
if err := r.doBuild(ctx, data); err != nil {
resp.Diagnostics.AddError("Client Error", err.Error())
return
}
data.Id = types.StringValue(id)
// Write logs using the tflog package
// Documentation: https://terraform.io/plugin/log
tflog.Trace(ctx, "created a resource")
// ID is the sha256 of the JSON-serialized input config,
// to ensure the resource is updated if the changes.
b, err := json.Marshal(data.Config.String())
if err != nil {
resp.Diagnostics.AddError("Client Error", err.Error())
return
}
data.Id = types.StringValue(fmt.Sprintf("%x", sha256.Sum256(b)))
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
@ -115,6 +117,15 @@ func (r *BuildResource) Read(ctx context.Context, req resource.ReadRequest, resp
return
}
// ID is the sha256 of the JSON-serialized input config,
// to ensure the resource is updated if the changes.
b, err := json.Marshal(data.Config.String())
if err != nil {
resp.Diagnostics.AddError("Client Error", err.Error())
return
}
data.Id = types.StringValue(fmt.Sprintf("%x", sha256.Sum256(b)))
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
@ -126,6 +137,20 @@ func (r *BuildResource) Update(ctx context.Context, req resource.UpdateRequest,
return
}
if err := r.doBuild(ctx, data); err != nil {
resp.Diagnostics.AddError("Client Error", err.Error())
return
}
// ID is the sha256 of the JSON-serialized input config,
// to ensure the resource is updated if the changes.
b, err := json.Marshal(data.Config.String())
if err != nil {
resp.Diagnostics.AddError("Client Error", err.Error())
return
}
data.Id = types.StringValue(fmt.Sprintf("%x", sha256.Sum256(b)))
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
@ -136,16 +161,17 @@ func (r *BuildResource) Delete(ctx context.Context, req resource.DeleteRequest,
if resp.Diagnostics.HasError() {
return
}
// Nothing to delete.
}
func (r *BuildResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
func (r *BuildResource) doBuild(ctx context.Context, data BuildResourceModel) (string, error) {
func (r *BuildResource) doBuild(ctx context.Context, data BuildResourceModel) error {
var cfg Configuration
if diags := reflect.AssignValue(data.Config, &cfg); diags.HasError() {
return "", fmt.Errorf("assigning value: %v", diags.Errors())
return fmt.Errorf("assigning value: %v", diags.Errors())
}
// TODO: support force-overwrite, so you don't have to rm the package or bump the epoch while testing locally.
@ -164,20 +190,20 @@ func (r *BuildResource) doBuild(ctx context.Context, data BuildResourceModel) (s
sdir := filepath.Join(r.popts.dir, cfg.Package.Name)
if _, err := os.Stat(sdir); os.IsNotExist(err) {
if err := os.MkdirAll(sdir, os.ModePerm); err != nil {
return "", fmt.Errorf("creating source directory %s: %v", sdir, err)
return fmt.Errorf("creating source directory %s: %v", sdir, err)
}
} else if err != nil {
return "", fmt.Errorf("creating source directory: %v", err)
return fmt.Errorf("creating source directory: %v", err)
}
// Write the config to a temp file.
// TODO(jason): This is kind of gross, but Melange's build API requires a file path.
tmp, err := os.CreateTemp("", fmt.Sprintf("%s-*.yaml", cfg.Package.Name))
if err != nil {
return "", fmt.Errorf("creating temporary file: %v", err)
return fmt.Errorf("creating temporary file: %v", err)
}
if err := os.WriteFile(tmp.Name(), []byte(data.ConfigContents.ValueString()), 0644); err != nil {
return "", fmt.Errorf("writing config to temporary file: %v", err)
return fmt.Errorf("writing config to temporary file: %v", err)
}
tflog.Trace(ctx, fmt.Sprintf("will build %s for %s", cfg.Package.Name, arch))
@ -195,7 +221,7 @@ func (r *BuildResource) doBuild(ctx context.Context, data BuildResourceModel) (s
build.WithGenerateIndex(true),
)
if err != nil {
return "", fmt.Errorf("building %s for %s: %w", cfg.Package.Name, arch, err)
return fmt.Errorf("building %s for %s: %w", cfg.Package.Name, arch, err)
}
bcs = append(bcs, bc)
}
@ -207,14 +233,7 @@ func (r *BuildResource) doBuild(ctx context.Context, data BuildResourceModel) (s
})
}
if err := errg.Wait(); err != nil {
return "", err
return err
}
// ID is the sha256 of the JSON-serialized input config,
// to ensure the resource is updated if the changes.
b, err := json.Marshal(data.Config)
if err != nil {
return "", err
}
return fmt.Sprintf("%x", sha256.Sum256(b)), nil
return nil
}

View file

@ -49,7 +49,8 @@ resource "melange_build" "build" {
}`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("melange_build.build", "config.package.name", "minimal"),
//resource.TestCheckResourceAttr("melange_build.build", "id", "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"),
resource.TestCheckResourceAttr("melange_build.build", "config.package.epoch", "3"),
resource.TestCheckResourceAttr("melange_build.build", "id", "2966ff23365b2bfb14798021109424102419b989b044778d5484297ade1b201b"),
),
}},
})
@ -90,4 +91,56 @@ resource "melange_build" "build" {
t.Errorf("checksum mismatch: %v != %v", idx.Packages[0].Checksum, pkg.Checksum)
}
// TODO(jason): Check that index is signed with the key.
// Update the resource to bump the epoch.
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: providerFactories,
Steps: []resource.TestStep{{
Config: `
data "melange_config" "minimal" {
config_contents = file("${path.module}/testdata/minimal.yaml")
}
// Simulate bumping the epoch.
locals {
updated = merge(data.melange_config.minimal.config, {
package = {
name = "minimal"
version = "0.0.1"
epoch = 4
}
})
}
resource "melange_build" "build" {
config = local.updated
config_contents = yamlencode(local.updated)
}`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("melange_build.build", "config.package.name", "minimal"),
resource.TestCheckResourceAttr("melange_build.build", "config.package.epoch", "4"),
resource.TestCheckResourceAttr("melange_build.build", "id", "1e44061d052789d741503299f4bb0fa37311dfb9df24f373994e6b95c0fe664c"),
),
}},
})
// Check the new apk.
{
fn := fmt.Sprintf("packages/%s/minimal-0.0.1-r4.apk", arch)
f, err := os.Open(fn)
if err != nil {
t.Fatalf("failed to open apk: %v", err)
}
defer f.Close()
pkg, err := repository.ParsePackage(f)
if err != nil {
t.Fatalf("failed to parse apk: %v", err)
}
if pkg.Name != "minimal" {
t.Errorf("unexpected package name: %v", pkg.Name)
}
if pkg.Version != "0.0.1-r4" {
t.Errorf("unexpected package version: %v", pkg.Version)
}
}
}