Skip to content

Commit 56c6a7f

Browse files
committed
image: Implement incus image alias nested block
Signed-off-by: Ruihua Wen <[email protected]>
1 parent 1811a53 commit 56c6a7f

File tree

5 files changed

+159
-436
lines changed

5 files changed

+159
-436
lines changed

docs/resources/image_alias.md

Lines changed: 0 additions & 33 deletions
This file was deleted.

internal/image/resource_image.go

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type ImageModel struct {
4242
SourceImage types.Object `tfsdk:"source_image"`
4343
SourceInstance types.Object `tfsdk:"source_instance"`
4444
Aliases types.Set `tfsdk:"aliases"`
45+
Alias types.Set `tfsdk:"alias"`
4546
Project types.String `tfsdk:"project"`
4647
Remote types.String `tfsdk:"remote"`
4748

@@ -70,6 +71,11 @@ type SourceInstanceModel struct {
7071
Snapshot types.String `tfsdk:"snapshot"`
7172
}
7273

74+
type ImageAliasModel struct {
75+
Name types.String `tfsdk:"name"`
76+
Description types.String `tfsdk:"description"`
77+
}
78+
7379
// ImageResource represent Incus cached image resource.
7480
type ImageResource struct {
7581
provider *provider_config.IncusProviderConfig
@@ -226,6 +232,36 @@ func (r ImageResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
226232
ElementType: types.StringType,
227233
},
228234
},
235+
236+
Blocks: map[string]schema.Block{
237+
"alias": schema.SetNestedBlock{
238+
Description: "Image alias",
239+
NestedObject: schema.NestedBlockObject{
240+
Attributes: map[string]schema.Attribute{
241+
"name": schema.StringAttribute{
242+
Required: true,
243+
Description: "Image alias name",
244+
Validators: []validator.String{
245+
stringvalidator.LengthAtLeast(1),
246+
},
247+
},
248+
"description": schema.StringAttribute{
249+
Optional: true,
250+
Description: "Image alias description",
251+
PlanModifiers: []planmodifier.String{
252+
stringplanmodifier.RequiresReplace(),
253+
},
254+
Validators: []validator.String{
255+
stringvalidator.LengthAtLeast(1),
256+
},
257+
},
258+
},
259+
PlanModifiers: []planmodifier.Object{
260+
objectplanmodifier.RequiresReplace(),
261+
},
262+
},
263+
},
264+
},
229265
}
230266
}
231267

@@ -422,8 +458,6 @@ func (r ImageResource) SyncState(ctx context.Context, tfState *tfsdk.State, serv
422458
return respDiags
423459
}
424460

425-
originalStateAliases := m.Aliases
426-
427461
if !m.SourceImage.IsNull() {
428462
var sourceImageModel SourceImageModel
429463
respDiags = m.SourceImage.As(ctx, &sourceImageModel, basetypes.ObjectAsOptions{})
@@ -453,22 +487,33 @@ func (r ImageResource) SyncState(ctx context.Context, tfState *tfsdk.State, serv
453487
copiedAliases, diags := ToAliasList(ctx, m.CopiedAliases)
454488
respDiags.Append(diags...)
455489

490+
configAliasBlocks, diags := ToAliasList(ctx, m.Alias)
491+
respDiags.Append(diags...)
492+
456493
// Copy aliases from image state that are present in user defined
457494
// config or are not copied.
458495
var aliases []string
496+
var aliasBlocks []api.ImageAlias
459497
for _, a := range image.Aliases {
460498
if utils.ValueInSlice(a.Name, configAliases) || !utils.ValueInSlice(a.Name, copiedAliases) {
499+
if utils.ValueInSlice(a.Name, configAliasBlocks) {
500+
aliasBlocks = append(aliasBlocks, a)
501+
}
502+
461503
aliases = append(aliases, a.Name)
462504
}
463505
}
464506

465507
aliasSet, diags := ToAliasSetType(ctx, aliases)
466508
respDiags.Append(diags...)
467509

510+
aliasBlocksSet, diags := ToAliasBlocksSetType(ctx, aliasBlocks)
511+
respDiags.Append(diags...)
512+
468513
m.Fingerprint = types.StringValue(image.Fingerprint)
469514
m.CreatedAt = types.Int64Value(image.CreatedAt.Unix())
470515
m.Aliases = aliasSet
471-
m.Aliases = originalStateAliases
516+
m.Alias = aliasBlocksSet
472517

473518
if respDiags.HasError() {
474519
return respDiags
@@ -571,7 +616,16 @@ func (r ImageResource) createImageFromSourceFile(ctx context.Context, resp *reso
571616
return
572617
}
573618

574-
imageAliases := make([]api.ImageAlias, 0, len(aliases))
619+
var imageAliasBlocks []ImageAliasModel
620+
if !plan.Alias.IsNull() {
621+
diags = plan.Alias.ElementsAs(ctx, &imageAliasBlocks, false)
622+
if diags.HasError() {
623+
resp.Diagnostics.Append(diags...)
624+
return
625+
}
626+
}
627+
628+
imageAliases := make([]api.ImageAlias, 0, len(aliases)+len(imageAliasBlocks))
575629
for _, alias := range aliases {
576630
// Ensure image alias does not already exist.
577631
aliasTarget, _, _ := server.GetImageAlias(alias)
@@ -586,6 +640,26 @@ func (r ImageResource) createImageFromSourceFile(ctx context.Context, resp *reso
586640

587641
imageAliases = append(imageAliases, ia)
588642
}
643+
644+
for _, imageAliasBlock := range imageAliasBlocks {
645+
// Ensure image alias does not already exist.
646+
name := imageAliasBlock.Name.ValueString()
647+
description := imageAliasBlock.Description.ValueString()
648+
649+
aliasTarget, _, _ := server.GetImageAlias(name)
650+
if aliasTarget != nil {
651+
resp.Diagnostics.AddError(fmt.Sprintf("Image alias %q already exists", name), "")
652+
return
653+
}
654+
655+
ia := api.ImageAlias{
656+
Name: name,
657+
Description: description,
658+
}
659+
660+
imageAliases = append(imageAliases, ia)
661+
}
662+
589663
image.Aliases = imageAliases
590664

591665
op, err := server.CreateImage(image, createArgs)
@@ -693,6 +767,15 @@ func (r ImageResource) createImageFromSourceImage(ctx context.Context, resp *res
693767
return
694768
}
695769

770+
var imageAliasBlocks []ImageAliasModel
771+
if !plan.Alias.IsNull() {
772+
diags = plan.Alias.ElementsAs(ctx, &imageAliasBlocks, false)
773+
if diags.HasError() {
774+
resp.Diagnostics.Append(diags...)
775+
return
776+
}
777+
}
778+
696779
imageAliases := make([]api.ImageAlias, 0, len(aliases))
697780
for _, alias := range aliases {
698781
// Ensure image alias does not already exist.
@@ -709,6 +792,25 @@ func (r ImageResource) createImageFromSourceImage(ctx context.Context, resp *res
709792
imageAliases = append(imageAliases, ia)
710793
}
711794

795+
for _, imageAliasBlock := range imageAliasBlocks {
796+
// Ensure image alias does not already exist.
797+
name := imageAliasBlock.Name.ValueString()
798+
description := imageAliasBlock.Description.ValueString()
799+
800+
aliasTarget, _, _ := server.GetImageAlias(name)
801+
if aliasTarget != nil {
802+
resp.Diagnostics.AddError(fmt.Sprintf("Image alias %q already exists", name), "")
803+
return
804+
}
805+
806+
ia := api.ImageAlias{
807+
Name: name,
808+
Description: description,
809+
}
810+
811+
imageAliases = append(imageAliases, ia)
812+
}
813+
712814
// Get data about remote image (also checks if image exists).
713815
imageInfo, _, err := imageServer.GetImage(image)
714816
if err != nil {
@@ -807,6 +909,15 @@ func (r ImageResource) createImageFromSourceInstance(ctx context.Context, resp *
807909
return
808910
}
809911

912+
var imageAliasBlocks []ImageAliasModel
913+
if !plan.Alias.IsNull() {
914+
diags = plan.Alias.ElementsAs(ctx, &imageAliasBlocks, false)
915+
if diags.HasError() {
916+
resp.Diagnostics.Append(diags...)
917+
return
918+
}
919+
}
920+
810921
imageAliases := make([]api.ImageAlias, 0, len(aliases))
811922
for _, alias := range aliases {
812923
// Ensure image alias does not already exist.
@@ -823,6 +934,25 @@ func (r ImageResource) createImageFromSourceInstance(ctx context.Context, resp *
823934
imageAliases = append(imageAliases, ia)
824935
}
825936

937+
for _, imageAliasBlock := range imageAliasBlocks {
938+
// Ensure image alias does not already exist.
939+
name := imageAliasBlock.Name.ValueString()
940+
description := imageAliasBlock.Description.ValueString()
941+
942+
aliasTarget, _, _ := server.GetImageAlias(name)
943+
if aliasTarget != nil {
944+
resp.Diagnostics.AddError(fmt.Sprintf("Image alias %q already exists", name), "")
945+
return
946+
}
947+
948+
ia := api.ImageAlias{
949+
Name: name,
950+
Description: description,
951+
}
952+
953+
imageAliases = append(imageAliases, ia)
954+
}
955+
826956
var source *api.ImagesPostSource
827957
if !sourceInstanceModel.Snapshot.IsNull() {
828958
snapsnotName := sourceInstanceModel.Snapshot.ValueString()
@@ -888,6 +1018,31 @@ func ToAliasSetType(ctx context.Context, aliases []string) (types.Set, diag.Diag
8881018
return types.SetValueFrom(ctx, types.StringType, aliases)
8891019
}
8901020

1021+
// ToAliasBlocksSetType converts slice of strings into aliases of type types.Set.
1022+
func ToAliasBlocksSetType(ctx context.Context, aliases []api.ImageAlias) (types.Set, diag.Diagnostics) {
1023+
aliasType := map[string]attr.Type{
1024+
"name": types.StringType,
1025+
"description": types.StringType,
1026+
}
1027+
1028+
aliasList := make([]ImageAliasModel, 0, len(aliases))
1029+
for _, a := range aliases {
1030+
alias := ImageAliasModel{
1031+
Name: types.StringValue(a.Name),
1032+
}
1033+
1034+
if a.Description != "" {
1035+
alias.Description = types.StringValue(a.Description)
1036+
} else {
1037+
alias.Description = types.StringNull()
1038+
}
1039+
1040+
aliasList = append(aliasList, alias)
1041+
}
1042+
1043+
return types.SetValueFrom(ctx, types.ObjectType{AttrTypes: aliasType}, aliasList)
1044+
}
1045+
8911046
// createImageResourceID creates new image ID by concatenating remote and
8921047
// image fingerprint using colon.
8931048
func createImageResourceID(remote string, fingerprint string) string {

0 commit comments

Comments
 (0)