@@ -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.
7480type 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.
8931048func createImageResourceID (remote string , fingerprint string ) string {
0 commit comments