Skip to content

Commit

Permalink
Add catalog tier to repo. (#169)
Browse files Browse the repository at this point in the history
Adds API field to set catalog tier for images (e.g. APPLICATION, BASE,
etc.)

Also fixes a bug w/ SyncConfig where protobuf string time was being
returned instead of RFC3339 (not sure why this wasn't caught before, but
was causing the tests to fail).
  • Loading branch information
wlynch authored Aug 9, 2024
1 parent fb4a823 commit 47ebe4f
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 12 deletions.
1 change: 1 addition & 0 deletions docs/resources/image_repo.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ resource "chainguard_image_repo" "example" {
- `bundles` (List of String) List of bundles associated with this repo (a-z freeform keywords for sales purposes).
- `readme` (String) The README for this repo.
- `sync_config` (Block, Optional) Configuration for catalog syncing. (see [below for nested schema](#nestedblock--sync_config))
- `tier` (String) Image tier associated with this repo.

### Read-Only

Expand Down
51 changes: 40 additions & 11 deletions internal/provider/resource_image_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ type imageRepoResourceModel struct {
Bundles types.List `tfsdk:"bundles"`
Readme types.String `tfsdk:"readme"`
SyncConfig types.Object `tfsdk:"sync_config"`
// Image tier (e.g. APPLICATION, BASE, etc.)
Tier types.String `tfsdk:"tier"`
}

type syncConfig struct {
Expand Down Expand Up @@ -112,6 +114,13 @@ func (r *imageRepoResource) Schema(_ context.Context, _ resource.SchemaRequest,
validators.ValidateStringFuncs(validReadmeValue),
},
},
"tier": schema.StringAttribute{
Description: "Image tier associated with this repo.",
Optional: true,
Validators: []validator.String{
validators.ValidateStringFuncs(validTierValue),
},
},
},
Blocks: map[string]schema.Block{
"sync_config": schema.SingleNestedBlock{
Expand Down Expand Up @@ -164,6 +173,14 @@ func validBundlesValue(s string) error {
return nil
}

// validTierValue implements validators.ValidateStringFunc.
func validTierValue(s string) error {
if _, ok := registry.CatalogTier_value[s]; !ok {
return fmt.Errorf("tier %q is invalid", s)
}
return nil
}

// validReadmeValue implements validators.ValidateStringFunc.
func validReadmeValue(s string) error {
if diff, err := validation.ValidateReadme(s); err != nil {
Expand Down Expand Up @@ -225,10 +242,11 @@ func (r *imageRepoResource) Create(ctx context.Context, req resource.CreateReque
repo, err := r.prov.client.Registry().Registry().CreateRepo(ctx, &registry.CreateRepoRequest{
ParentId: plan.ParentID.ValueString(),
Repo: &registry.Repo{
Name: plan.Name.ValueString(),
Bundles: bundles,
Readme: plan.Readme.ValueString(),
SyncConfig: sc,
Name: plan.Name.ValueString(),
Bundles: bundles,
Readme: plan.Readme.ValueString(),
SyncConfig: sc,
CatalogTier: registry.CatalogTier(registry.CatalogTier_value[plan.Tier.ValueString()]),
},
})
if err != nil {
Expand Down Expand Up @@ -286,6 +304,10 @@ func (r *imageRepoResource) Read(ctx context.Context, req resource.ReadRequest,
state.Readme = types.StringValue(repo.Readme)
}

if !(state.Tier.IsNull() && repo.CatalogTier == registry.CatalogTier_UNKNOWN) {
state.Tier = types.StringValue(repo.CatalogTier.String())
}

var sc syncConfig
var diags diag.Diagnostics
if !state.SyncConfig.IsNull() {
Expand All @@ -294,13 +316,13 @@ func (r *imageRepoResource) Read(ctx context.Context, req resource.ReadRequest,
return
}
update := (sc.Source.ValueString() != repo.SyncConfig.Source) ||
(sc.Expiration.ValueString() != repo.SyncConfig.Expiration.String()) ||
(sc.Expiration.ValueString() != repo.SyncConfig.Expiration.AsTime().Format(time.RFC3339)) ||
(sc.UniqueTags.ValueBool() != repo.SyncConfig.UniqueTags) ||
(sc.SyncAPKs.ValueBool() != repo.SyncConfig.SyncApks)

if update {
sc.Source = types.StringValue(repo.SyncConfig.Source)
sc.Expiration = types.StringValue(repo.SyncConfig.Expiration.String())
sc.Expiration = types.StringValue(repo.SyncConfig.Expiration.AsTime().Format(time.RFC3339))
sc.UniqueTags = types.BoolValue(repo.SyncConfig.UniqueTags)
sc.SyncAPKs = types.BoolValue(repo.SyncConfig.SyncApks)
state.SyncConfig, diags = types.ObjectValueFrom(ctx, state.SyncConfig.AttributeTypes(ctx), sc)
Expand Down Expand Up @@ -361,11 +383,12 @@ func (r *imageRepoResource) Update(ctx context.Context, req resource.UpdateReque
return
}
repo, err := r.prov.client.Registry().Registry().UpdateRepo(ctx, &registry.Repo{
Id: data.ID.ValueString(),
Name: data.Name.ValueString(),
Bundles: bundles,
Readme: data.Readme.ValueString(),
SyncConfig: sc,
Id: data.ID.ValueString(),
Name: data.Name.ValueString(),
Bundles: bundles,
Readme: data.Readme.ValueString(),
SyncConfig: sc,
CatalogTier: registry.CatalogTier(registry.CatalogTier_value[data.Tier.ValueString()]),
})
if err != nil {
resp.Diagnostics.Append(errorToDiagnostic(err, "failed to update image repo"))
Expand All @@ -380,6 +403,12 @@ func (r *imageRepoResource) Update(ctx context.Context, req resource.UpdateReque
if repo.Readme != "" {
data.Readme = types.StringValue(repo.Readme)
}
// Treat UNKNOWN tier as null, but only if it was already null
if !(data.Tier.IsNull() && repo.CatalogTier == registry.CatalogTier_UNKNOWN) {
data.Tier = types.StringValue(repo.CatalogTier.String())
} else {
data.Tier = types.StringNull()
}

var diags diag.Diagnostics
data.Bundles, diags = types.ListValueFrom(ctx, types.StringType, repo.Bundles)
Expand Down
14 changes: 13 additions & 1 deletion internal/provider/resource_image_repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type testRepo struct {
synced bool
unique bool
apks bool
tier string
}

func TestImageRepo(t *testing.T) {
Expand All @@ -42,6 +43,7 @@ func TestImageRepo(t *testing.T) {
name: name,
bundles: `["a", "b", "c"]`,
readme: "# hello",
tier: "APPLICATION",
}

// Modify bundles and readme
Expand All @@ -50,6 +52,7 @@ func TestImageRepo(t *testing.T) {
name: name,
bundles: `["x", "y", "z"]`,
readme: "# goodbye",
tier: "BASE",
}

// Delete readme and bundles, add syncing
Expand Down Expand Up @@ -81,6 +84,7 @@ func TestImageRepo(t *testing.T) {
resource.TestCheckResourceAttr(`chainguard_image_repo.example`, `name`, name),
resource.TestCheckNoResourceAttr(`chainguard_image_repo.example`, `bundles`),
resource.TestCheckNoResourceAttr(`chainguard_image_repo.example`, `readme`),
resource.TestCheckNoResourceAttr(`chainguard_image_repo.example`, `tier`),
),
},

Expand All @@ -101,6 +105,7 @@ func TestImageRepo(t *testing.T) {
resource.TestCheckResourceAttr(`chainguard_image_repo.example`, `bundles.1`, "b"),
resource.TestCheckResourceAttr(`chainguard_image_repo.example`, `bundles.2`, "c"),
resource.TestCheckResourceAttr(`chainguard_image_repo.example`, `readme`, "# hello"),
resource.TestCheckResourceAttr(`chainguard_image_repo.example`, `tier`, "APPLICATION"),
),
},

Expand All @@ -114,6 +119,7 @@ func TestImageRepo(t *testing.T) {
resource.TestCheckResourceAttr(`chainguard_image_repo.example`, `bundles.1`, "y"),
resource.TestCheckResourceAttr(`chainguard_image_repo.example`, `bundles.2`, "z"),
resource.TestCheckResourceAttr(`chainguard_image_repo.example`, `readme`, "# goodbye"),
resource.TestCheckResourceAttr(`chainguard_image_repo.example`, `tier`, "BASE"),
),
},

Expand Down Expand Up @@ -175,6 +181,7 @@ resource "chainguard_image_repo" "example" {
%s
%s
%s
%s
}
`
var bundlesLine string
Expand All @@ -197,7 +204,12 @@ resource "chainguard_image_repo" "example" {
}`, time.Now().Add(24*time.Hour).UTC().Format(time.RFC3339), repo.unique, repo.apks)
}

return fmt.Sprintf(tmpl, repo.parentID, repo.parentID, repo.name, bundlesLine, readmeLine, syncLine)
var tierLine string
if repo.tier != "" {
tierLine = fmt.Sprintf("tier = %q", repo.tier)
}

return fmt.Sprintf(tmpl, repo.parentID, repo.parentID, repo.name, bundlesLine, readmeLine, syncLine, tierLine)
}

// Multiple equivalent concurrent updates should not cause errors.
Expand Down

0 comments on commit 47ebe4f

Please sign in to comment.