Skip to content

mantle/platform/azure: Add support for Azure Shared Image Gallery (SIG) and other enhancements #4109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions mantle/cmd/kola/kola.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,15 @@ func writeProps() error {
InstanceType string `json:"type"`
}
type Azure struct {
DiskURI string `json:"diskUri"`
Publisher string `json:"publisher"`
Offer string `json:"offer"`
Sku string `json:"sku"`
Version string `json:"version"`
Location string `json:"location"`
Size string `json:"size"`
DiskURI string `json:"diskUri"`
Publisher string `json:"publisher"`
Offer string `json:"offer"`
Sku string `json:"sku"`
Version string `json:"version"`
Location string `json:"location"`
Size string `json:"size"`
AvailabilityZone string `json:"availability_zone"`
HyperVGeneration string `json:"hyper_v_generation"`
}
type DO struct {
Region string `json:"region"`
Expand Down Expand Up @@ -355,13 +357,15 @@ func writeProps() error {
InstanceType: kola.AWSOptions.InstanceType,
},
Azure: Azure{
DiskURI: kola.AzureOptions.DiskURI,
Publisher: kola.AzureOptions.Publisher,
Offer: kola.AzureOptions.Offer,
Sku: kola.AzureOptions.Sku,
Version: kola.AzureOptions.Version,
Location: kola.AzureOptions.Location,
Size: kola.AzureOptions.Size,
DiskURI: kola.AzureOptions.DiskURI,
Publisher: kola.AzureOptions.Publisher,
Offer: kola.AzureOptions.Offer,
Sku: kola.AzureOptions.Sku,
Version: kola.AzureOptions.Version,
Location: kola.AzureOptions.Location,
Size: kola.AzureOptions.Size,
AvailabilityZone: kola.AzureOptions.AvailabilityZone,
HyperVGeneration: kola.AzureOptions.HyperVGeneration,
},
DO: DO{
Region: kola.DOOptions.Region,
Expand Down
2 changes: 2 additions & 0 deletions mantle/cmd/kola/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ func init() {
sv(&kola.AzureOptions.Version, "azure-version", "", "Azure image version")
sv(&kola.AzureOptions.Location, "azure-location", "westus", "Azure location (default \"westus\"")
sv(&kola.AzureOptions.Size, "azure-size", "Standard_D2_v2", "Azure machine size (default \"Standard_D2_v2\")")
sv(&kola.AzureOptions.AvailabilityZone, "azure-availability-zone", "1", "Azure Availability Zone (default \"1\")")
sv(&kola.AzureOptions.HyperVGeneration, "azure-hyper-v-generation", "V1", "Azure Hyper-V Generation (default \"V1\")")

// do-specific options
sv(&kola.DOOptions.ConfigPath, "do-config-file", "", "DigitalOcean config file (default \"~/"+auth.DOConfigPath+"\")")
Expand Down
6 changes: 6 additions & 0 deletions mantle/cmd/ore/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ var (

azureCredentials string
azureLocation string
azureHyperVGen string
azurePublisher string

api *azure.API
)
Expand All @@ -44,6 +46,8 @@ func init() {
sv := Azure.PersistentFlags().StringVar
sv(&azureCredentials, "azure-credentials", "", "Azure credentials file location (default \"~/"+auth.AzureCredentialsPath+"\")")
sv(&azureLocation, "azure-location", "westus", "Azure location (default \"westus\")")
sv(&azureHyperVGen, "azure-hyper-v-generation", "V1", "Azure Hypervisor Generation")
sv(&azurePublisher, "azure-publisher", "CoreOS", "Azure image publisher")
}

func preauth(cmd *cobra.Command, args []string) error {
Expand All @@ -52,6 +56,8 @@ func preauth(cmd *cobra.Command, args []string) error {
a, err := azure.New(&azure.Options{
AzureCredentials: azureCredentials,
Location: azureLocation,
HyperVGeneration: azureHyperVGen,
Publisher: azurePublisher,
})
if err != nil {
plog.Fatalf("Failed to create Azure API: %v", err)
Expand Down
99 changes: 99 additions & 0 deletions mantle/cmd/ore/azure/create-gallery-image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2025 Red Hat
// Copyright 2018 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package azure

import (
"encoding/json"
"fmt"
"os"

"github.com/spf13/cobra"
)

var (
cmdCreateGalleryImage = &cobra.Command{
Use: "create-gallery-image",
Short: "Create Azure Gallery image",
Long: "Create Azure Gallery image from a blob url",
RunE: runCreateGalleryImage,
Aliases: []string{"create-gallery-image-arm"},

SilenceUsage: true,
}

galleryImageName string
galleryName string
sourceImageId string
)

func init() {
sv := cmdCreateGalleryImage.Flags().StringVar

sv(&galleryImageName, "gallery-image-name", "", "gallery image name")
sv(&galleryName, "gallery-name", "kola", "gallery name")
sv(&blobUrl, "image-blob", "", "source blob url")
sv(&sourceImageId, "source-image", "", "source Azure disk uri")
sv(&resourceGroup, "resource-group", "kola", "resource group name")

Azure.AddCommand(cmdCreateGalleryImage)
}

func runCreateGalleryImage(cmd *cobra.Command, args []string) error {
if sourceImageId != "" && blobUrl != "" {
fmt.Fprintf(os.Stderr, "cannot use --image-blob with --source-image\n")
os.Exit(1)
}

if err := api.SetupClients(); err != nil {
fmt.Fprintf(os.Stderr, "setting up clients: %v\n", err)
os.Exit(1)
}

if sourceImageId == "" {
img, err := api.CreateImage(galleryImageName, resourceGroup, blobUrl)
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't create Azure image: %v\n", err)
os.Exit(1)
}
if img.ID == nil {
fmt.Fprintf(os.Stderr, "received nil image\n")
os.Exit(1)
}
sourceImageId = *img.ID
}

galleryImage, err := api.CreateGalleryImage(galleryImageName, galleryName, resourceGroup, sourceImageId)
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't create Azure Shared Image Gallery image: %v\n", err)
os.Exit(1)
}
if galleryImage.ID == nil {
fmt.Fprintf(os.Stderr, "received nil gallery image\n")
os.Exit(1)
}
err = json.NewEncoder(os.Stdout).Encode(&struct {
ID *string
Location *string
}{
ID: galleryImage.ID,
Location: galleryImage.Location,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't encode result: %v\n", err)
os.Exit(1)
}
return nil
}
69 changes: 69 additions & 0 deletions mantle/cmd/ore/azure/delete-gallery-image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2025 Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package azure

import (
"fmt"

"github.com/spf13/cobra"
)

var (
cmdDeleteGalleryImage = &cobra.Command{
Use: "delete-gallery-image",
Short: "Delete Azure Gallery image",
Long: "Remove a Shared Image Gallery image from Azure.",
RunE: runDeleteGalleryImage,
Aliases: []string{"delete-image-arm"},

SilenceUsage: true,
}

deleteGallery bool
)

func init() {
sv := cmdDeleteGalleryImage.Flags().StringVar
bv := cmdDeleteGalleryImage.Flags().BoolVar

sv(&imageName, "gallery-image-name", "", "gallery image name")
sv(&resourceGroup, "resource-group", "kola", "resource group name")
sv(&galleryName, "gallery-name", "kola", "gallery name")
bv(&deleteGallery, "delete-gallery", false, "delete entire gallery")

Azure.AddCommand(cmdDeleteGalleryImage)
}

func runDeleteGalleryImage(cmd *cobra.Command, args []string) error {
if err := api.SetupClients(); err != nil {
return fmt.Errorf("setting up clients: %v\n", err)
}

if deleteGallery {
err := api.DeleteGallery(galleryName, resourceGroup)
if err != nil {
return fmt.Errorf("Couldn't delete gallery: %v\n", err)
}
return nil
}

err := api.DeleteGalleryImage(imageName, resourceGroup, galleryName)
if err != nil {
return fmt.Errorf("Couldn't delete gallery image: %v\n", err)
}

plog.Printf("Image %q in gallery %q in resource group %q removed", imageName, galleryName, resourceGroup)
return nil
}
3 changes: 3 additions & 0 deletions mantle/kola/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,7 @@ type externalTestMeta struct {
Conflicts []string `json:"conflicts" yaml:"conflicts"`
AllowConfigWarnings bool `json:"allowConfigWarnings" yaml:"allowConfigWarnings"`
NoInstanceCreds bool `json:"noInstanceCreds" yaml:"noInstanceCreds"`
InstanceType string `json:"instanceType" yaml:"instanceType"`
Description string `json:"description" yaml:"description"`
}

Expand Down Expand Up @@ -1236,6 +1237,7 @@ ExecStart=%s
AdditionalNics: targetMeta.AdditionalNics,
AppendKernelArgs: targetMeta.AppendKernelArgs,
AppendFirstbootKernelArgs: targetMeta.AppendFirstbootKernelArgs,
InstanceType: targetMeta.InstanceType,
NonExclusive: !targetMeta.Exclusive,
Conflicts: targetMeta.Conflicts,

Expand Down Expand Up @@ -1757,6 +1759,7 @@ func runTest(h *harness.H, t *register.Test, pltfrm string, flight platform.Flig
AppendKernelArgs: t.AppendKernelArgs,
AppendFirstbootKernelArgs: t.AppendFirstbootKernelArgs,
SkipStartMachine: true,
InstanceType: t.InstanceType,
}

// Providers sometimes fail to bring up a machine within a
Expand Down
4 changes: 4 additions & 0 deletions mantle/kola/register/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ type Test struct {
// Conflicts is non-empty iff nonexclusive is true
// Contains the tests that conflict with this particular test
Conflicts []string

// If provided, this test will be run on the target instance type.
// This overrides the instance type set with `kola run`
InstanceType string
}

// Registered tests that run as part of `kola run` live here. Mapping of names
Expand Down
50 changes: 40 additions & 10 deletions mantle/platform/api/azure/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,21 @@ import (
)

type API struct {
azIdCred *azidentity.DefaultAzureCredential
rgClient *armresources.ResourceGroupsClient
imgClient *armcompute.ImagesClient
compClient *armcompute.VirtualMachinesClient
netClient *armnetwork.VirtualNetworksClient
subClient *armnetwork.SubnetsClient
ipClient *armnetwork.PublicIPAddressesClient
intClient *armnetwork.InterfacesClient
accClient *armstorage.AccountsClient
opts *Options
azIdCred *azidentity.DefaultAzureCredential
rgClient *armresources.ResourceGroupsClient
imgClient *armcompute.ImagesClient
compClient *armcompute.VirtualMachinesClient
galClient *armcompute.GalleriesClient
galImgClient *armcompute.GalleryImagesClient
galImgVerClient *armcompute.GalleryImageVersionsClient
diskClient *armcompute.DisksClient
netClient *armnetwork.VirtualNetworksClient
subClient *armnetwork.SubnetsClient
ipClient *armnetwork.PublicIPAddressesClient
intClient *armnetwork.InterfacesClient
nsgClient *armnetwork.SecurityGroupsClient
accClient *armstorage.AccountsClient
opts *Options
}

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

a.galClient, err = armcompute.NewGalleriesClient(a.opts.SubscriptionID, a.azIdCred, nil)
if err != nil {
return err
}

a.galImgClient, err = armcompute.NewGalleryImagesClient(a.opts.SubscriptionID, a.azIdCred, nil)
if err != nil {
return err
}

a.galImgVerClient, err = armcompute.NewGalleryImageVersionsClient(a.opts.SubscriptionID, a.azIdCred, nil)
if err != nil {
return err
}

a.diskClient, err = armcompute.NewDisksClient(a.opts.SubscriptionID, a.azIdCred, nil)
if err != nil {
return err
}

a.netClient, err = armnetwork.NewVirtualNetworksClient(a.opts.SubscriptionID, a.azIdCred, nil)
if err != nil {
return err
Expand All @@ -109,6 +134,11 @@ func (a *API) SetupClients() error {
return err
}

a.nsgClient, err = armnetwork.NewSecurityGroupsClient(a.opts.SubscriptionID, a.azIdCred, nil)
if err != nil {
return err
}

a.accClient, err = armstorage.NewAccountsClient(a.opts.SubscriptionID, a.azIdCred, nil)
return err
}
Expand Down
Loading
Loading