Skip to content

Commit b0c667e

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 19926aa commit b0c667e

File tree

9 files changed

+496
-13
lines changed

9 files changed

+496
-13
lines changed

mantle/cmd/kola/kola.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ func writeProps() error {
308308
Location string `json:"location"`
309309
Size string `json:"size"`
310310
AvailabilityZone string `json:"availability_zone"`
311+
HyperVGeneration string `json:"hyper_v_generation"`
311312
}
312313
type DO struct {
313314
Region string `json:"region"`
@@ -364,6 +365,7 @@ func writeProps() error {
364365
Location: kola.AzureOptions.Location,
365366
Size: kola.AzureOptions.Size,
366367
AvailabilityZone: kola.AzureOptions.AvailabilityZone,
368+
HyperVGeneration: kola.AzureOptions.HyperVGeneration,
367369
},
368370
DO: DO{
369371
Region: kola.DOOptions.Region,

mantle/cmd/kola/options.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func init() {
101101
sv(&kola.AzureOptions.Location, "azure-location", "westus", "Azure location (default \"westus\"")
102102
sv(&kola.AzureOptions.Size, "azure-size", "Standard_D2_v2", "Azure machine size (default \"Standard_D2_v2\")")
103103
sv(&kola.AzureOptions.AvailabilityZone, "azure-availability-zone", "1", "Azure Availability Zone (default \"1\")")
104+
sv(&kola.AzureOptions.HyperVGeneration, "azure-hyper-v-generation", "V1", "Azure Hyper-V Generation (default \"V1\")")
104105

105106
// do-specific options
106107
sv(&kola.DOOptions.ConfigPath, "do-config-file", "", "DigitalOcean config file (default \"~/"+auth.DOConfigPath+"\")")

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: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
sourceImageId string
40+
)
41+
42+
func init() {
43+
sv := cmdCreateGalleryImage.Flags().StringVar
44+
45+
sv(&galleryImageName, "gallery-image-name", "", "gallery image name")
46+
sv(&galleryName, "gallery-name", "kola", "gallery name")
47+
sv(&blobUrl, "image-blob", "", "source blob url")
48+
sv(&sourceImageId, "source-image", "", "source Azure disk uri")
49+
sv(&resourceGroup, "resource-group", "kola", "resource group name")
50+
51+
Azure.AddCommand(cmdCreateGalleryImage)
52+
}
53+
54+
func runCreateGalleryImage(cmd *cobra.Command, args []string) error {
55+
if sourceImageId != "" && blobUrl != "" {
56+
fmt.Fprintf(os.Stderr, "cannot use --image-blob with --source-image\n")
57+
os.Exit(1)
58+
}
59+
60+
if err := api.SetupClients(); err != nil {
61+
fmt.Fprintf(os.Stderr, "setting up clients: %v\n", err)
62+
os.Exit(1)
63+
}
64+
65+
if sourceImageId == "" {
66+
img, err := api.CreateImage(galleryImageName, resourceGroup, blobUrl)
67+
if err != nil {
68+
fmt.Fprintf(os.Stderr, "Couldn't create Azure image: %v\n", err)
69+
os.Exit(1)
70+
}
71+
if img.ID == nil {
72+
fmt.Fprintf(os.Stderr, "received nil image\n")
73+
os.Exit(1)
74+
}
75+
sourceImageId = *img.ID
76+
}
77+
78+
galleryImage, err := api.CreateGalleryImage(galleryImageName, galleryName, resourceGroup, sourceImageId)
79+
if err != nil {
80+
fmt.Fprintf(os.Stderr, "Couldn't create Azure Shared Image Gallery image: %v\n", err)
81+
os.Exit(1)
82+
}
83+
if galleryImage.ID == nil {
84+
fmt.Fprintf(os.Stderr, "received nil gallery image\n")
85+
os.Exit(1)
86+
}
87+
err = json.NewEncoder(os.Stdout).Encode(&struct {
88+
ID *string
89+
Location *string
90+
}{
91+
ID: galleryImage.ID,
92+
Location: galleryImage.Location,
93+
})
94+
if err != nil {
95+
fmt.Fprintf(os.Stderr, "Couldn't encode result: %v\n", err)
96+
os.Exit(1)
97+
}
98+
return nil
99+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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-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-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+
return nil
60+
}
61+
62+
err := api.DeleteGalleryImage(imageName, resourceGroup, galleryName)
63+
if err != nil {
64+
return fmt.Errorf("Couldn't delete gallery image: %v\n", err)
65+
}
66+
67+
plog.Printf("Image %q in gallery %q in resource group %q removed", imageName, galleryName, resourceGroup)
68+
return nil
69+
}

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)