Skip to content

Commit cf0130d

Browse files
committed
ore/azure: add options to create and delete Shared Image Gallery Images
Add a new `create-gallery-image` ore command to Support creating images within Azure Shared Image Galleries (Gallery Images). The command creates image definitions and versions in Azure Shared Image Galleries. Gallery images can be created from either a blob URL or an existing managed image. A `--azure-publisher` flag is added to assign a publisher to the gallery image. `delete-gallery-image` is also added to delete individual gallery images or an entire Shared Image Gallery.
1 parent 2f394bf commit cf0130d

File tree

7 files changed

+497
-13
lines changed

7 files changed

+497
-13
lines changed

mantle/cmd/ore/azure/azure.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var (
3535
azureCredentials string
3636
azureLocation string
3737
azureHyperVGen string
38+
azurePublisher string
3839

3940
api *azure.API
4041
)
@@ -46,6 +47,7 @@ func init() {
4647
sv(&azureCredentials, "azure-credentials", "", "Azure credentials file location (default \"~/"+auth.AzureCredentialsPath+"\")")
4748
sv(&azureLocation, "azure-location", "westus", "Azure location (default \"westus\")")
4849
sv(&azureHyperVGen, "azure-hyper-v-generation", "V1", "Azure Hypervisor Generation")
50+
sv(&azurePublisher, "azure-publisher", "CoreOS", "Azure image publisher")
4951
}
5052

5153
func preauth(cmd *cobra.Command, args []string) error {
@@ -55,6 +57,7 @@ func preauth(cmd *cobra.Command, args []string) error {
5557
AzureCredentials: azureCredentials,
5658
Location: azureLocation,
5759
HyperVGeneration: azureHyperVGen,
60+
Publisher: azurePublisher,
5861
})
5962
if err != nil {
6063
plog.Fatalf("Failed to create Azure API: %v", err)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2025 Red Hat
2+
// Copyright 2018 CoreOS, Inc.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package azure
17+
18+
import (
19+
"encoding/json"
20+
"fmt"
21+
"os"
22+
23+
"github.com/spf13/cobra"
24+
)
25+
26+
var (
27+
cmdCreateGalleryImage = &cobra.Command{
28+
Use: "create-gallery-image",
29+
Short: "Create Azure Gallery image",
30+
Long: "Create Azure Gallery image from a blob url",
31+
RunE: runCreateGalleryImage,
32+
Aliases: []string{"create-gallery-image-arm"},
33+
34+
SilenceUsage: true,
35+
}
36+
37+
galleryImageName string
38+
galleryName string
39+
)
40+
41+
func init() {
42+
sv := cmdCreateGalleryImage.Flags().StringVar
43+
44+
sv(&galleryImageName, "gallery-image-name", "", "gallery image name")
45+
sv(&galleryName, "gallery-name", "kola", "gallery name")
46+
sv(&blobUrl, "image-blob", "", "source blob url")
47+
sv(&resourceGroup, "resource-group", "kola", "resource group name")
48+
49+
Azure.AddCommand(cmdCreateGalleryImage)
50+
}
51+
52+
func runCreateGalleryImage(cmd *cobra.Command, args []string) error {
53+
if blobUrl == "" {
54+
fmt.Fprintf(os.Stderr, "must supply --image-blob\n")
55+
os.Exit(1)
56+
}
57+
58+
if err := api.SetupClients(); err != nil {
59+
fmt.Fprintf(os.Stderr, "setting up clients: %v\n", err)
60+
os.Exit(1)
61+
}
62+
63+
img, err := api.CreateImage(galleryImageName, resourceGroup, blobUrl)
64+
if err != nil {
65+
fmt.Fprintf(os.Stderr, "Couldn't create Azure image: %v\n", err)
66+
os.Exit(1)
67+
}
68+
if img.ID == nil {
69+
fmt.Fprintf(os.Stderr, "received nil image\n")
70+
os.Exit(1)
71+
}
72+
sourceImageId := *img.ID
73+
74+
galleryImage, err := api.CreateGalleryImage(galleryImageName, galleryName, resourceGroup, sourceImageId)
75+
if err != nil {
76+
fmt.Fprintf(os.Stderr, "Couldn't create Azure Shared Image Gallery image: %v\n", err)
77+
os.Exit(1)
78+
}
79+
if galleryImage.ID == nil {
80+
fmt.Fprintf(os.Stderr, "received nil gallery image\n")
81+
os.Exit(1)
82+
}
83+
err = json.NewEncoder(os.Stdout).Encode(&struct {
84+
ID *string
85+
Location *string
86+
}{
87+
ID: galleryImage.ID,
88+
Location: galleryImage.Location,
89+
})
90+
if err != nil {
91+
fmt.Fprintf(os.Stderr, "Couldn't encode result: %v\n", err)
92+
os.Exit(1)
93+
}
94+
return nil
95+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2025 Red Hat
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package azure
16+
17+
import (
18+
"fmt"
19+
20+
"github.com/spf13/cobra"
21+
)
22+
23+
var (
24+
cmdDeleteGalleryImage = &cobra.Command{
25+
Use: "delete-gallery-image",
26+
Short: "Delete Azure Gallery image",
27+
Long: "Remove a Shared Image Gallery image from Azure.",
28+
RunE: runDeleteGalleryImage,
29+
Aliases: []string{"delete-gallery-image-arm"},
30+
31+
SilenceUsage: true,
32+
}
33+
34+
deleteGallery bool
35+
)
36+
37+
func init() {
38+
sv := cmdDeleteGalleryImage.Flags().StringVar
39+
bv := cmdDeleteGalleryImage.Flags().BoolVar
40+
41+
sv(&imageName, "gallery-image-name", "", "gallery image name")
42+
sv(&resourceGroup, "resource-group", "kola", "resource group name")
43+
sv(&galleryName, "gallery-name", "kola", "gallery name")
44+
bv(&deleteGallery, "delete-entire-gallery", false, "delete entire gallery")
45+
46+
Azure.AddCommand(cmdDeleteGalleryImage)
47+
}
48+
49+
func runDeleteGalleryImage(cmd *cobra.Command, args []string) error {
50+
if err := api.SetupClients(); err != nil {
51+
return fmt.Errorf("setting up clients: %v\n", err)
52+
}
53+
54+
if deleteGallery {
55+
err := api.DeleteGallery(galleryName, resourceGroup)
56+
if err != nil {
57+
return fmt.Errorf("Couldn't delete gallery: %v\n", err)
58+
}
59+
plog.Printf("Gallery %q in resource group %q removed", galleryName, resourceGroup)
60+
return nil
61+
}
62+
63+
err := api.DeleteGalleryImage(imageName, resourceGroup, galleryName)
64+
if err != nil {
65+
return fmt.Errorf("Couldn't delete gallery image: %v\n", err)
66+
}
67+
68+
// Gallery image versions are backed by managed images with the same name,
69+
// so we can easily identify and delete them together.
70+
err = api.DeleteImage(imageName, resourceGroup)
71+
if err != nil {
72+
return fmt.Errorf("Couldn't delete image: %v\n", err)
73+
}
74+
75+
plog.Printf("Image %q in gallery %q in resource group %q removed", imageName, galleryName, resourceGroup)
76+
return nil
77+
}

mantle/platform/api/azure/api.go

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,21 @@ import (
3131
)
3232

3333
type API struct {
34-
azIdCred *azidentity.DefaultAzureCredential
35-
rgClient *armresources.ResourceGroupsClient
36-
imgClient *armcompute.ImagesClient
37-
compClient *armcompute.VirtualMachinesClient
38-
netClient *armnetwork.VirtualNetworksClient
39-
subClient *armnetwork.SubnetsClient
40-
ipClient *armnetwork.PublicIPAddressesClient
41-
intClient *armnetwork.InterfacesClient
42-
accClient *armstorage.AccountsClient
43-
opts *Options
34+
azIdCred *azidentity.DefaultAzureCredential
35+
rgClient *armresources.ResourceGroupsClient
36+
imgClient *armcompute.ImagesClient
37+
compClient *armcompute.VirtualMachinesClient
38+
galClient *armcompute.GalleriesClient
39+
galImgClient *armcompute.GalleryImagesClient
40+
galImgVerClient *armcompute.GalleryImageVersionsClient
41+
diskClient *armcompute.DisksClient
42+
netClient *armnetwork.VirtualNetworksClient
43+
subClient *armnetwork.SubnetsClient
44+
ipClient *armnetwork.PublicIPAddressesClient
45+
intClient *armnetwork.InterfacesClient
46+
nsgClient *armnetwork.SecurityGroupsClient
47+
accClient *armstorage.AccountsClient
48+
opts *Options
4449
}
4550

4651
// New creates a new Azure client. If no publish settings file is provided or
@@ -89,6 +94,26 @@ func (a *API) SetupClients() error {
8994
return err
9095
}
9196

97+
a.galClient, err = armcompute.NewGalleriesClient(a.opts.SubscriptionID, a.azIdCred, nil)
98+
if err != nil {
99+
return err
100+
}
101+
102+
a.galImgClient, err = armcompute.NewGalleryImagesClient(a.opts.SubscriptionID, a.azIdCred, nil)
103+
if err != nil {
104+
return err
105+
}
106+
107+
a.galImgVerClient, err = armcompute.NewGalleryImageVersionsClient(a.opts.SubscriptionID, a.azIdCred, nil)
108+
if err != nil {
109+
return err
110+
}
111+
112+
a.diskClient, err = armcompute.NewDisksClient(a.opts.SubscriptionID, a.azIdCred, nil)
113+
if err != nil {
114+
return err
115+
}
116+
92117
a.netClient, err = armnetwork.NewVirtualNetworksClient(a.opts.SubscriptionID, a.azIdCred, nil)
93118
if err != nil {
94119
return err
@@ -109,6 +134,11 @@ func (a *API) SetupClients() error {
109134
return err
110135
}
111136

137+
a.nsgClient, err = armnetwork.NewSecurityGroupsClient(a.opts.SubscriptionID, a.azIdCred, nil)
138+
if err != nil {
139+
return err
140+
}
141+
112142
a.accClient, err = armstorage.NewAccountsClient(a.opts.SubscriptionID, a.azIdCred, nil)
113143
return err
114144
}

0 commit comments

Comments
 (0)