diff --git a/cmd/boot-aws/main.go b/cmd/boot-aws/main.go index 7f7675fe03..35c753d59d 100644 --- a/cmd/boot-aws/main.go +++ b/cmd/boot-aws/main.go @@ -1,14 +1,11 @@ package main import ( - "bytes" "encoding/json" "fmt" "io" "os" - "os/exec" "path/filepath" - "strings" "time" "github.com/google/uuid" @@ -16,6 +13,7 @@ import ( "github.com/spf13/pflag" "github.com/osbuild/images/internal/common" + "github.com/osbuild/images/internal/test" "github.com/osbuild/images/pkg/arch" "github.com/osbuild/images/pkg/cloud/awscloud" "github.com/osbuild/images/pkg/platform" @@ -56,28 +54,6 @@ type resources struct { InstanceID *string `json:"instance,omitempty"` } -func run(c string, args ...string) ([]byte, []byte, error) { - fmt.Printf("> %s %s\n", c, strings.Join(args, " ")) - cmd := exec.Command(c, args...) - - var cmdout, cmderr bytes.Buffer - cmd.Stdout = &cmdout - cmd.Stderr = &cmderr - err := cmd.Run() - - // print any output even if the call failed - stdout := cmdout.Bytes() - if len(stdout) > 0 { - fmt.Println(string(stdout)) - } - - stderr := cmderr.Bytes() - if len(stderr) > 0 { - fmt.Fprintf(os.Stderr, "%s\n", string(stderr)) - } - return stdout, stderr, err -} - func getInstanceType(arch string) (string, error) { switch arch { case "x86_64": @@ -89,52 +65,6 @@ func getInstanceType(arch string) (string, error) { } } -func sshRun(ip, user, key, hostsfile string, command ...string) error { - sshargs := []string{"-i", key, "-o", fmt.Sprintf("UserKnownHostsFile=%s", hostsfile), "-l", user, ip} - sshargs = append(sshargs, command...) - _, _, err := run("ssh", sshargs...) - if err != nil { - return err - } - return nil -} - -func scpFile(ip, user, key, hostsfile, source, dest string) error { - _, _, err := run("scp", "-i", key, "-o", fmt.Sprintf("UserKnownHostsFile=%s", hostsfile), "--", source, fmt.Sprintf("%s@%s:%s", user, ip, dest)) - if err != nil { - return err - } - return nil -} - -func keyscan(ip, filepath string) error { - var keys []byte - maxTries := 30 // wait for at least 5 mins - var keyscanErr error - for try := 0; try < maxTries; try++ { - keys, _, keyscanErr = run("ssh-keyscan", ip) - if keyscanErr == nil { - break - } - time.Sleep(10 * time.Second) - } - if keyscanErr != nil { - return keyscanErr - } - - fmt.Printf("Creating known hosts file: %s\n", filepath) - hostsFile, err := os.Create(filepath) - if err != nil { - return err - } - - fmt.Printf("Writing to known hosts file: %s\n", filepath) - if _, err := hostsFile.Write(keys); err != nil { - return err - } - return nil -} - func newClientFromArgs(flags *pflag.FlagSet) (*awscloud.AWS, error) { region, err := flags.GetString("region") if err != nil { @@ -415,12 +345,12 @@ func doRunExec(a *awscloud.AWS, command []string, flags *pflag.FlagSet, res *res if err != nil { return err } - if err := keyscan(ip, hostsfile); err != nil { + if err := test.Keyscan(ip, hostsfile); err != nil { return err } // ssh into the remote machine and exit immediately to check connection - if err := sshRun(ip, username, privKey, hostsfile, "exit"); err != nil { + if err := test.SshRun(ip, username, privKey, hostsfile, "exit"); err != nil { return err } @@ -446,7 +376,7 @@ func doRunExec(a *awscloud.AWS, command []string, flags *pflag.FlagSet, res *res // scp the file and add it to the remote command by its base name remotePath := filepath.Base(arg) remoteCommand[idx] = remotePath - if err := scpFile(ip, username, privKey, hostsfile, arg, remotePath); err != nil { + if err := test.ScpFile(ip, username, privKey, hostsfile, arg, remotePath); err != nil { return err } } else { @@ -459,7 +389,7 @@ func doRunExec(a *awscloud.AWS, command []string, flags *pflag.FlagSet, res *res remoteCommand[0] = fmt.Sprintf("./%s", remoteCommand[0]) // run the executable - return sshRun(ip, username, privKey, hostsfile, remoteCommand...) + return test.SshRun(ip, username, privKey, hostsfile, remoteCommand...) } func runExec(cmd *cobra.Command, args []string) { diff --git a/cmd/boot-azure/main.go b/cmd/boot-azure/main.go new file mode 100644 index 0000000000..d15a5fdcc0 --- /dev/null +++ b/cmd/boot-azure/main.go @@ -0,0 +1,576 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/osbuild/images/internal/test" + "github.com/osbuild/images/pkg/cloud/azure" +) + +const ( + StorageContainer = "images" +) + +// exitCheck can be deferred from the top of command functions to exit with an +// error code after any other defers are run in the same scope. +func exitCheck(err error) { + if err != nil { + fmt.Fprint(os.Stderr, err.Error()+"\n") + os.Exit(1) + } +} + +// resources created or allocated for an instance that can be cleaned up when +// tearing down. +type resources struct { + VM *azure.VM `json:"vm,omitempty"` + BlobName *string `json:"blob,omitempty"` + ImageName *string `json:"image,omitempty"` +} + +func newClientFromArgs(flags *pflag.FlagSet) (*azure.Client, error) { + client, err := flags.GetString("client-id") + if err != nil { + return nil, err + } + secret, err := flags.GetString("client-secret") + if err != nil { + return nil, err + } + tenant, err := flags.GetString("tenant") + if err != nil { + return nil, err + } + subscr, err := flags.GetString("subscription") + if err != nil { + return nil, err + } + + return azure.NewClient( + azure.Credentials{ + ClientID: client, + ClientSecret: secret, + }, + tenant, + subscr, + ) +} + +func getDefaultSize(arch string) (string, error) { + switch arch { + case "x86_64": + return "Standard_DS1_v2", nil + case "aarch64": + return "Standard_D2pls_v5", nil + default: + return "", fmt.Errorf("getDefaultSize(): unknown architecture %q", arch) + } +} + +func upload(ac *azure.Client, subscription, resourceGroup, localImage, remoteName string, res *resources) (string, error) { + ctx := context.Background() + location, err := ac.GetResourceGroupLocation(ctx, resourceGroup) + if err != nil { + return "", err + } + + staccTag := azure.Tag{ + Name: "imagesStorageAccount", + Value: fmt.Sprintf("location=%s", location), + } + stacc, err := ac.GetResourceNameByTag( + ctx, + resourceGroup, + staccTag, + ) + if err != nil { + return "", err + } + + if stacc == "" { + stacc = azure.RandomStorageAccountName("images") + err = ac.CreateStorageAccount(ctx, resourceGroup, stacc, "", staccTag) + if err != nil { + return "", err + } + } + + storekey, err := ac.GetStorageAccountKey(ctx, resourceGroup, stacc) + if err != nil { + return "", err + } + + storeClient, err := azure.NewStorageClient(stacc, storekey) + if err != nil { + return "", err + } + + err = storeClient.CreateStorageContainerIfNotExist(ctx, stacc, StorageContainer) + if err != nil { + return "", err + } + blobName := azure.EnsureVHDExtension(remoteName) + err = storeClient.UploadPageBlob( + azure.BlobMetadata{ + StorageAccount: stacc, + ContainerName: StorageContainer, + BlobName: blobName, + }, + localImage, + azure.DefaultUploadThreads, + ) + if err != nil { + return "", err + } + res.BlobName = &blobName + + err = ac.RegisterImage( + ctx, + resourceGroup, + stacc, + StorageContainer, + blobName, + remoteName, + "", + azure.HyperVGenV2, + ) + if err != nil { + return "", err + } + res.ImageName = &remoteName + image := fmt.Sprintf( + "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", + subscription, resourceGroup, remoteName) + return image, nil +} + +func doSetup(ac *azure.Client, flags *pflag.FlagSet, localImage string, res *resources) error { + rg, err := flags.GetString("resource-group") + if err != nil { + return err + } + + image, err := flags.GetString("image") + if err != nil { + return err + } + + if localImage != "" { + subscription, err := flags.GetString("subscription") + if err != nil { + return err + } + remoteImageName, err := flags.GetString("image-name") + if err != nil { + return err + } + image, err = upload(ac, subscription, rg, localImage, remoteImageName, res) + if err != nil { + return err + } + } + + vmName, err := flags.GetString("vm-name") + if err != nil { + return err + } + + size, err := flags.GetString("size") + if err != nil { + return err + } + if size == "" { + arch, err := flags.GetString("arch") + if err != nil { + return err + } + size, err = getDefaultSize(arch) + if err != nil { + return err + } + } + + username, err := flags.GetString("username") + if err != nil { + return err + } + + keyPath, err := flags.GetString("ssh-pubkey") + if err != nil { + return err + } + + keyfile, err := os.Open(keyPath) + if err != nil { + return err + } + defer func() { + if err := keyfile.Close(); err != nil { + fmt.Fprintf(os.Stderr, "unable to close public key file: %s\n", err.Error()) + } + }() + + keyData, err := io.ReadAll(keyfile) + if err != nil { + return err + } + + ctx := context.Background() + vm, err := ac.CreateVM( + ctx, + rg, + image, + vmName, + size, + username, + string(keyData), + ) + if err != nil { + return err + } + res.VM = vm + return nil +} + +func setup(cmd *cobra.Command, args []string) { + var fnerr error + defer func() { exitCheck(fnerr) }() + + flags := cmd.Flags() + + ac, err := newClientFromArgs(flags) + if err != nil { + fnerr = err + return + } + + localImage := "" + if len(args) == 1 { + localImage = args[0] + } + + res := &resources{} + fnerr = doSetup(ac, flags, localImage, res) + if fnerr != nil { + fmt.Fprintf(os.Stderr, "setup() failed: %s\n", fnerr.Error()) + fmt.Fprint(os.Stderr, "tearing down resources\n") + + rg, err := flags.GetString("resource-group") + if err != nil { + fnerr = fmt.Errorf("failed to get resource group to tear down resources: %s", err.Error()) + return + } + + if err := doTeardown(ac, rg, res); err != nil { + fnerr = fmt.Errorf("failed to tear down resources: %s", err.Error()) + return + } + } + + resourcesFile, err := flags.GetString("resourcefile") + if err != nil { + fnerr = err + return + } + resfile, err := os.Create(resourcesFile) + if err != nil { + fnerr = err + return + } + defer func() { + if err := resfile.Close(); err != nil { + fmt.Fprintf(os.Stderr, "unable to close resources file: %s\n", err.Error()) + } + }() + + resdata, err := json.MarshalIndent(res, "", " ") + if err != nil { + fnerr = err + return + } + _, err = resfile.Write(resdata) + if err != nil { + fnerr = err + return + } +} + +func doTeardown(ac *azure.Client, resourceGroup string, res *resources) error { + ctx := context.Background() + + if res.VM != nil { + if err := ac.DestroyVM(ctx, res.VM); err != nil { + return err + } + } + + if res.ImageName != nil { + if err := ac.DeleteImage(ctx, resourceGroup, *res.ImageName); err != nil { + return err + } + } + + if res.BlobName == nil { + return nil + } + + location, err := ac.GetResourceGroupLocation(ctx, resourceGroup) + if err != nil { + return err + } + + staccTag := azure.Tag{ + Name: "imagesStorageAccount", + Value: fmt.Sprintf("location=%s", location), + } + stacc, err := ac.GetResourceNameByTag( + ctx, + resourceGroup, + staccTag, + ) + if err != nil { + return err + } + + // storage account no longer exists, so assume everything is gone + if stacc == "" { + return nil + } + + storekey, err := ac.GetStorageAccountKey(ctx, resourceGroup, stacc) + if err != nil { + return err + } + + storeClient, err := azure.NewStorageClient(stacc, storekey) + if err != nil { + return err + } + + if err := storeClient.DeleteBlob(ctx, azure.BlobMetadata{ + StorageAccount: stacc, + ContainerName: StorageContainer, + BlobName: *res.BlobName, + }); err != nil { + return err + } + return nil +} + +func teardown(cmd *cobra.Command, args []string) { + var fnerr error + defer func() { exitCheck(fnerr) }() + + flags := cmd.Flags() + ac, err := newClientFromArgs(flags) + if err != nil { + fnerr = err + return + } + + resourcesFile, err := flags.GetString("resourcefile") + if err != nil { + return + } + + res := &resources{} + resfile, err := os.Open(resourcesFile) + if err != nil { + fnerr = fmt.Errorf("failed to open resources file: %s", err.Error()) + return + } + defer func() { + if err := resfile.Close(); err != nil { + fmt.Fprintf(os.Stderr, "unable to close resources file: %s\n", err.Error()) + } + }() + + resdata, err := io.ReadAll(resfile) + if err != nil { + fnerr = fmt.Errorf("failed to read resources file: %s", err.Error()) + return + } + if err := json.Unmarshal(resdata, res); err != nil { + fnerr = fmt.Errorf("failed to unmarshal resources data: %s", err.Error()) + return + } + + rg, err := flags.GetString("resource-group") + if err != nil { + fnerr = fmt.Errorf("failed to get resource group: %s", err.Error()) + return + } + + fnerr = doTeardown(ac, rg, res) +} + +func doRunExec(ac *azure.Client, command []string, flags *pflag.FlagSet, res *resources) error { + privKey, err := flags.GetString("ssh-privkey") + if err != nil { + return err + } + + username, err := flags.GetString("username") + if err != nil { + return err + } + + tmpdir, err := os.MkdirTemp("", "boot-test-*") + if err != nil { + return err + } + defer os.RemoveAll(tmpdir) + + hostsfile := filepath.Join(tmpdir, "known_hosts") + ip := res.VM.IPAddress + if err := test.Keyscan(ip, hostsfile); err != nil { + return err + } + + // ssh into the remote machine and exit immediately to check connection + if err := test.SshRun(ip, username, privKey, hostsfile, "exit"); err != nil { + return err + } + + isFile := func(path string) bool { + fileInfo, err := os.Stat(path) + if err != nil { + // ignore error and assume it's not a path + return false + } + + // Check if it's a regular file + return fileInfo.Mode().IsRegular() + } + + // copy every argument that is a file to the remote host (basename only) + // and construct remote command + // NOTE: this wont work with directories or with multiple args in different + // paths that share the same basename - it's very limited + remoteCommand := make([]string, len(command)) + for idx := range command { + arg := command[idx] + if isFile(arg) { + // scp the file and add it to the remote command by its base name + remotePath := filepath.Base(arg) + remoteCommand[idx] = remotePath + if err := test.ScpFile(ip, username, privKey, hostsfile, arg, remotePath); err != nil { + return err + } + } else { + // not a file: add the arg as is + remoteCommand[idx] = arg + } + } + + // add ./ to first element for the executable + remoteCommand[0] = fmt.Sprintf("./%s", remoteCommand[0]) + + // run the executable + return test.SshRun(ip, username, privKey, hostsfile, remoteCommand...) +} + +func runExec(cmd *cobra.Command, args []string) { + var fnerr error + defer func() { exitCheck(fnerr) }() + image := args[0] + + command := args[1:] + flags := cmd.Flags() + + ac, fnerr := newClientFromArgs(flags) + if fnerr != nil { + return + } + + res := &resources{} + defer func() { + rg, err := flags.GetString("resource-group") + if err != nil { + fnerr = fmt.Errorf("failed to get resource group to tear down resources: %s", err.Error()) + return + } + if err := doTeardown(ac, rg, res); err != nil { + fnerr = fmt.Errorf("failed to destroy vm: %s", err.Error()) + return + } + }() + + fnerr = doSetup(ac, flags, image, res) + if fnerr != nil { + return + } + + fnerr = doRunExec(ac, command, flags, res) +} + +func setupCLI() *cobra.Command { + rootCmd := &cobra.Command{ + Use: "boot", + Long: "upload and boot an image to the appropriate cloud provider", + DisableFlagsInUseLine: true, + } + + rootFlags := rootCmd.PersistentFlags() + rootFlags.String("client-id", "", "client ID") + rootFlags.String("client-secret", "", "client secret") + rootFlags.String("tenant", "", "tenant id") + rootFlags.String("subscription", "", "subscription") + rootFlags.String("resource-group", "", "resource group of image and vm") + rootFlags.String("username", "azure", "name of the user to create on the system") + rootFlags.String("ssh-pubkey", "", "path to user's public ssh key, must be an rsa key") + rootFlags.String("ssh-privkey", "", "path to user's private ssh key") + rootFlags.String("image", "", "full resource ID of the remote image name, this should already exist in the resource group, if no local image is provided") + rootFlags.String("vm-name", "vm-name", "name of the VM to create, all dependencies will be prefixed with this name") + rootFlags.String("image-name", "image-name", "the image and blob will") + rootFlags.String("size", "", "size or instance type of the VM to create") + rootFlags.String("arch", "x86_64", "size or instance type of the VM to create") + + exitCheck(rootCmd.MarkPersistentFlagRequired("client-id")) + exitCheck(rootCmd.MarkPersistentFlagRequired("client-secret")) + exitCheck(rootCmd.MarkPersistentFlagRequired("tenant")) + exitCheck(rootCmd.MarkPersistentFlagRequired("subscription")) + + setupCmd := &cobra.Command{ + Use: "setup [--resourcefile ] ", + Short: "upload and boot an image and save the created resource IDs to a file for later teardown", + Args: cobra.MaximumNArgs(1), + Run: setup, + DisableFlagsInUseLine: true, + } + setupCmd.Flags().StringP("resourcefile", "r", "resources.json", "path to store the resource IDs") + rootCmd.AddCommand(setupCmd) + + teardownCmd := &cobra.Command{ + Use: "teardown [--resourcefile ]", + Short: "teardown (clean up) all the resources specified in a resources file created by a previous 'setup' call", + Args: cobra.NoArgs, + Run: teardown, + } + teardownCmd.Flags().StringP("resourcefile", "r", "resources.json", "path to store the resource IDs") + rootCmd.AddCommand(teardownCmd) + + runCmd := &cobra.Command{ + Use: "run ...", + Short: "upload and boot an image, then upload the specified executable and run it on the remote host", + Long: "upload and boot an image on Azure, then upload the executable file specified by the second positional argument and execute it via SSH with the args on the command line", + Args: cobra.MinimumNArgs(2), + Run: runExec, + } + rootCmd.AddCommand(runCmd) + + return rootCmd +} + +func main() { + cmd := setupCLI() + exitCheck(cmd.Execute()) +} diff --git a/go.mod b/go.mod index 7f9eb20483..121d8b0dfc 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v7 v7.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 diff --git a/go.sum b/go.sum index 51b206ed62..8ee7b7498b 100644 --- a/go.sum +++ b/go.sum @@ -41,6 +41,10 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsI github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.2.0 h1:HYGD75g0bQ3VO/Omedm54v4LrD3B1cGImuRF3AJ5wLo= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.2.0/go.mod h1:ulHyBFJOI0ONiRL4vcJTmS7rx18jQQlEPmAgo80cRdM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v7 v7.0.0 h1:qZQVUcgr3ZUsyt8lf4FS+Wjj1NxyPaMY7Nj/3UiFgO4= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v7 v7.0.0/go.mod h1:vbbC5kaJ8H3mz4GIXafT5thlUo2qzW46Zzl1dKKpZVk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= diff --git a/internal/test/boot_helpers.go b/internal/test/boot_helpers.go new file mode 100644 index 0000000000..5a45872544 --- /dev/null +++ b/internal/test/boot_helpers.go @@ -0,0 +1,78 @@ +package test + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "strings" + "time" +) + +func run(c string, args ...string) ([]byte, []byte, error) { + fmt.Printf("> %s %s\n", c, strings.Join(args, " ")) + cmd := exec.Command(c, args...) + + var cmdout, cmderr bytes.Buffer + cmd.Stdout = &cmdout + cmd.Stderr = &cmderr + err := cmd.Run() + + // print any output even if the call failed + stdout := cmdout.Bytes() + if len(stdout) > 0 { + fmt.Println(string(stdout)) + } + + stderr := cmderr.Bytes() + if len(stderr) > 0 { + fmt.Fprintf(os.Stderr, "%s\n", string(stderr)) + } + return stdout, stderr, err +} + +func SshRun(ip, user, key, hostsfile string, command ...string) error { + sshargs := []string{"-i", key, "-o", fmt.Sprintf("UserKnownHostsFile=%s", hostsfile), "-l", user, ip} + sshargs = append(sshargs, command...) + _, _, err := run("ssh", sshargs...) + if err != nil { + return err + } + return nil +} + +func ScpFile(ip, user, key, hostsfile, source, dest string) error { + _, _, err := run("scp", "-i", key, "-o", fmt.Sprintf("UserKnownHostsFile=%s", hostsfile), "--", source, fmt.Sprintf("%s@%s:%s", user, ip, dest)) + if err != nil { + return err + } + return nil +} + +func Keyscan(ip, filepath string) error { + var keys []byte + maxTries := 30 // wait for at least 5 mins + var keyscanErr error + for try := 0; try < maxTries; try++ { + keys, _, keyscanErr = run("ssh-keyscan", ip) + if keyscanErr == nil { + break + } + time.Sleep(10 * time.Second) + } + if keyscanErr != nil { + return keyscanErr + } + + fmt.Printf("Creating known hosts file: %s\n", filepath) + hostsFile, err := os.Create(filepath) + if err != nil { + return err + } + + fmt.Printf("Writing to known hosts file: %s\n", filepath) + if _, err := hostsFile.Write(keys); err != nil { + return err + } + return nil +} diff --git a/pkg/cloud/azure/azure.go b/pkg/cloud/azure/azure.go index cc48a72dcb..00ed8cd0d8 100644 --- a/pkg/cloud/azure/azure.go +++ b/pkg/cloud/azure/azure.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v7" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" @@ -26,15 +27,41 @@ type Client struct { resourceGroups ResourceGroupsClient accounts AccountsClient images ImagesClient + vms VMsClient + disks DisksClient + vnets VirtualNetworksClient + subnets SubnetsClient + securityGroups SecurityGroupsClient + publicIPs PublicIPsClient + interfaces InterfacesClient } -func newTestClient(rc ResourcesClient, rgc ResourceGroupsClient, ac AccountsClient, ic ImagesClient) *Client { +func newTestClient( + rc ResourcesClient, + rgc ResourceGroupsClient, + ac AccountsClient, + ic ImagesClient, + vnets VirtualNetworksClient, + subnets SubnetsClient, + pips PublicIPsClient, + sgs SecurityGroupsClient, + intfs InterfacesClient, + vms VMsClient, + disks DisksClient, +) *Client { return &Client{ creds: nil, resources: rc, resourceGroups: rgc, accounts: ac, images: ic, + vnets: vnets, + subnets: subnets, + publicIPs: pips, + securityGroups: sgs, + interfaces: intfs, + vms: vms, + disks: disks, } } @@ -42,7 +69,7 @@ func newTestClient(rc ResourcesClient, rgc ResourceGroupsClient, ac AccountsClie // See https://docs.microsoft.com/en-us/rest/api/azure/ // If you need to work with the Azure Storage API, see NewStorageClient func NewClient(credentials Credentials, tenantID, subscriptionID string) (*Client, error) { - creds, err := azidentity.NewClientSecretCredential(tenantID, credentials.clientID, credentials.clientSecret, nil) + creds, err := azidentity.NewClientSecretCredential(tenantID, credentials.ClientID, credentials.ClientSecret, nil) if err != nil { return nil, fmt.Errorf("creating azure ClientSecretCredential failed: %w", err) } @@ -62,12 +89,23 @@ func NewClient(credentials Credentials, tenantID, subscriptionID string) (*Clien return nil, fmt.Errorf("creating compute client factory failed: %w", err) } + networkFact, err := armnetwork.NewClientFactory(subscriptionID, creds, nil) + if err != nil { + return nil, fmt.Errorf("creating compute client factory failed: %w", err) + } return &Client{ creds, resFact.NewClient(), resFact.NewResourceGroupsClient(), storFact.NewAccountsClient(), compFact.NewImagesClient(), + compFact.NewVirtualMachinesClient(), + compFact.NewDisksClient(), + networkFact.NewVirtualNetworksClient(), + networkFact.NewSubnetsClient(), + networkFact.NewSecurityGroupsClient(), + networkFact.NewPublicIPAddressesClient(), + networkFact.NewInterfacesClient(), }, nil } @@ -212,3 +250,15 @@ func (ac Client) RegisterImage(ctx context.Context, resourceGroup, storageAccoun return nil } + +func (ac Client) DeleteImage(ctx context.Context, resourceGroup, imageName string) error { + poller, err := ac.images.BeginDelete(ctx, resourceGroup, imageName, nil) + if err != nil { + return err + } + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + return nil +} diff --git a/pkg/cloud/azure/azure_test.go b/pkg/cloud/azure/azure_test.go index 301f022155..aa9a026177 100644 --- a/pkg/cloud/azure/azure_test.go +++ b/pkg/cloud/azure/azure_test.go @@ -12,11 +12,18 @@ import ( ) type azm struct { - az *azure.Client - rcm *resourcesMock - rgm *resourceGroupsMock - acm *accountsMock - im *imagesMock + az *azure.Client + rcm *resourcesMock + rgm *resourceGroupsMock + acm *accountsMock + im *imagesMock + vnetm *vnetMock + snm *subnetMock + pipm *pipMock + sgm *sgMock + intfm *intfMock + vmm *vmMock + diskm *diskMock } func newAZ() azm { @@ -24,12 +31,26 @@ func newAZ() azm { rgm := &resourceGroupsMock{} acm := &accountsMock{} im := &imagesMock{} + vnetm := &vnetMock{} + snm := &subnetMock{} + pipm := &pipMock{} + sgm := &sgMock{} + intfm := &intfMock{} + vmm := &vmMock{} + diskm := &diskMock{} return azm{ - azure.NewTestclient(rcm, rgm, acm, im), + azure.NewTestclient(rcm, rgm, acm, im, vnetm, snm, pipm, sgm, intfm, vmm, diskm), rcm, rgm, acm, im, + vnetm, + snm, + pipm, + sgm, + intfm, + vmm, + diskm, } } @@ -132,3 +153,15 @@ func TestRegisterImage(t *testing.T) { Location: common.ToPtr("test-universe"), }, azm.im.createOrUpdate[0].img) } + +func TestDeleteImage(t *testing.T) { + azm := newAZ() + err := azm.az.DeleteImage(context.Background(), "rg", "imgname") + require.NoError(t, err) + + require.Len(t, azm.im.delete, 1) + require.Equal(t, imDeleteArgs{ + rg: "rg", + name: "imgname", + }, azm.im.delete[0]) +} diff --git a/pkg/cloud/azure/azurestorage.go b/pkg/cloud/azure/azurestorage.go index 7479c60d12..76d608093a 100644 --- a/pkg/cloud/azure/azurestorage.go +++ b/pkg/cloud/azure/azurestorage.go @@ -249,6 +249,19 @@ func (c StorageClient) TagBlob(ctx context.Context, metadata BlobMetadata, tags return nil } +func (c StorageClient) DeleteBlob(ctx context.Context, metadata BlobMetadata) error { + URL, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s/%s", metadata.StorageAccount, metadata.ContainerName, metadata.BlobName)) + client, err := blob.NewClientWithSharedKeyCredential(URL.String(), c.credential, nil) + if err != nil { + return fmt.Errorf("cannot create a blob client: %w", err) + } + _, err = client.Delete(ctx, nil) + if err != nil { + return fmt.Errorf("cannot delete the blob: %w", err) + } + return nil +} + // RandomStorageAccountName returns a randomly generated name that can be used // for a storage account. This means that it must use only alphanumeric // characters and its length must be 24 or lower. diff --git a/pkg/cloud/azure/client-interfaces.go b/pkg/cloud/azure/client-interfaces.go index 149c094cb8..29616aa941 100644 --- a/pkg/cloud/azure/client-interfaces.go +++ b/pkg/cloud/azure/client-interfaces.go @@ -5,6 +5,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v7" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" ) @@ -24,4 +25,39 @@ type AccountsClient interface { type ImagesClient interface { BeginCreateOrUpdate(context.Context, string, string, armcompute.Image, *armcompute.ImagesClientBeginCreateOrUpdateOptions) (*runtime.Poller[armcompute.ImagesClientCreateOrUpdateResponse], error) + BeginDelete(context.Context, string, string, *armcompute.ImagesClientBeginDeleteOptions) (*runtime.Poller[armcompute.ImagesClientDeleteResponse], error) +} + +type VMsClient interface { + BeginCreateOrUpdate(context.Context, string, string, armcompute.VirtualMachine, *armcompute.VirtualMachinesClientBeginCreateOrUpdateOptions) (*runtime.Poller[armcompute.VirtualMachinesClientCreateOrUpdateResponse], error) + BeginDelete(context.Context, string, string, *armcompute.VirtualMachinesClientBeginDeleteOptions) (*runtime.Poller[armcompute.VirtualMachinesClientDeleteResponse], error) +} + +type DisksClient interface { + BeginDelete(context.Context, string, string, *armcompute.DisksClientBeginDeleteOptions) (*runtime.Poller[armcompute.DisksClientDeleteResponse], error) +} + +type VirtualNetworksClient interface { + BeginCreateOrUpdate(context.Context, string, string, armnetwork.VirtualNetwork, *armnetwork.VirtualNetworksClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.VirtualNetworksClientCreateOrUpdateResponse], error) + BeginDelete(context.Context, string, string, *armnetwork.VirtualNetworksClientBeginDeleteOptions) (*runtime.Poller[armnetwork.VirtualNetworksClientDeleteResponse], error) +} + +type SubnetsClient interface { + BeginCreateOrUpdate(context.Context, string, string, string, armnetwork.Subnet, *armnetwork.SubnetsClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.SubnetsClientCreateOrUpdateResponse], error) + BeginDelete(context.Context, string, string, string, *armnetwork.SubnetsClientBeginDeleteOptions) (*runtime.Poller[armnetwork.SubnetsClientDeleteResponse], error) +} + +type SecurityGroupsClient interface { + BeginCreateOrUpdate(context.Context, string, string, armnetwork.SecurityGroup, *armnetwork.SecurityGroupsClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.SecurityGroupsClientCreateOrUpdateResponse], error) + BeginDelete(context.Context, string, string, *armnetwork.SecurityGroupsClientBeginDeleteOptions) (*runtime.Poller[armnetwork.SecurityGroupsClientDeleteResponse], error) +} + +type PublicIPsClient interface { + BeginCreateOrUpdate(context.Context, string, string, armnetwork.PublicIPAddress, *armnetwork.PublicIPAddressesClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.PublicIPAddressesClientCreateOrUpdateResponse], error) + BeginDelete(context.Context, string, string, *armnetwork.PublicIPAddressesClientBeginDeleteOptions) (*runtime.Poller[armnetwork.PublicIPAddressesClientDeleteResponse], error) +} + +type InterfacesClient interface { + BeginCreateOrUpdate(context.Context, string, string, armnetwork.Interface, *armnetwork.InterfacesClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.InterfacesClientCreateOrUpdateResponse], error) + BeginDelete(context.Context, string, string, *armnetwork.InterfacesClientBeginDeleteOptions) (*runtime.Poller[armnetwork.InterfacesClientDeleteResponse], error) } diff --git a/pkg/cloud/azure/credentials.go b/pkg/cloud/azure/credentials.go index 752bf5a069..69b9f6e57d 100644 --- a/pkg/cloud/azure/credentials.go +++ b/pkg/cloud/azure/credentials.go @@ -7,8 +7,8 @@ import ( ) type Credentials struct { - clientID string - clientSecret string + ClientID string + ClientSecret string } // ParseAzureCredentialsFile parses a credentials file for azure. @@ -29,7 +29,7 @@ func ParseAzureCredentialsFile(filename string) (*Credentials, error) { } return &Credentials{ - clientID: creds.ClientID, - clientSecret: creds.ClientSecret, + ClientID: creds.ClientID, + ClientSecret: creds.ClientSecret, }, nil } diff --git a/pkg/cloud/azure/mocks_test.go b/pkg/cloud/azure/mocks_test.go index ebe194b9bb..9cbda77c76 100644 --- a/pkg/cloud/azure/mocks_test.go +++ b/pkg/cloud/azure/mocks_test.go @@ -6,6 +6,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v7" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" @@ -28,6 +29,19 @@ func (mp *mockPollerHandler[T]) Result(ctx context.Context, out *T) error { return nil } +func makePoller[T any](result *T) (*runtime.Poller[T], error) { + return runtime.NewPoller( + &http.Response{}, + runtime.NewPipeline("", "", runtime.PipelineOptions{}, nil), + &runtime.NewPollerOptions[T]{ + Handler: &mockPollerHandler[T]{ + result: result, + }, + Response: result, + }, + ) +} + type resourcesMock struct { list []rmListArgs } @@ -104,23 +118,13 @@ func (acm *accountsMock) BeginCreate( options *armstorage.AccountsClientBeginCreateOptions) (*runtime.Poller[armstorage.AccountsClientCreateResponse], error) { acm.beginCreate = append(acm.beginCreate, acmBeginCreateArgs{rg, account, params, options}) - p, err := runtime.NewPoller( - &http.Response{}, - runtime.NewPipeline("", "", runtime.PipelineOptions{}, nil), - &runtime.NewPollerOptions[armstorage.AccountsClientCreateResponse]{ - Handler: &mockPollerHandler[armstorage.AccountsClientCreateResponse]{ - result: &armstorage.AccountsClientCreateResponse{ - Account: armstorage.Account{ - Name: &account, - }, - }, + return makePoller[armstorage.AccountsClientCreateResponse]( + &armstorage.AccountsClientCreateResponse{ + Account: armstorage.Account{ + Name: &account, }, }, ) - if err != nil { - return nil, err - } - return p, nil } type acmListKeysArgs struct { @@ -149,6 +153,7 @@ func (acm *accountsMock) ListKeys( type imagesMock struct { createOrUpdate []imBeginCreateOrUpdateArgs + delete []imDeleteArgs } type imBeginCreateOrUpdateArgs struct { @@ -158,22 +163,273 @@ type imBeginCreateOrUpdateArgs struct { options *armcompute.ImagesClientBeginCreateOrUpdateOptions } +type imDeleteArgs struct { + rg string + name string + options *armcompute.ImagesClientBeginDeleteOptions +} + func (im *imagesMock) BeginCreateOrUpdate(ctx context.Context, rg string, name string, img armcompute.Image, options *armcompute.ImagesClientBeginCreateOrUpdateOptions) (*runtime.Poller[armcompute.ImagesClientCreateOrUpdateResponse], error) { im.createOrUpdate = append(im.createOrUpdate, imBeginCreateOrUpdateArgs{rg, name, img, options}) - p, err := runtime.NewPoller( - &http.Response{}, - runtime.NewPipeline("", "", runtime.PipelineOptions{}, nil), - &runtime.NewPollerOptions[armcompute.ImagesClientCreateOrUpdateResponse]{ - Handler: &mockPollerHandler[armcompute.ImagesClientCreateOrUpdateResponse]{ - result: &armcompute.ImagesClientCreateOrUpdateResponse{ - Image: img, + return makePoller[armcompute.ImagesClientCreateOrUpdateResponse]( + &armcompute.ImagesClientCreateOrUpdateResponse{ + Image: img, + }, + ) +} + +func (im *imagesMock) BeginDelete(ctx context.Context, rg string, name string, options *armcompute.ImagesClientBeginDeleteOptions) (*runtime.Poller[armcompute.ImagesClientDeleteResponse], error) { + im.delete = append(im.delete, imDeleteArgs{rg, name, options}) + + return makePoller[armcompute.ImagesClientDeleteResponse]( + &armcompute.ImagesClientDeleteResponse{}, + ) +} + +type vnetMock struct { + createOrUpdate []vnetCreateOrUpdateArgs + delete []vnetDeleteArgs +} + +type vnetCreateOrUpdateArgs struct { + rg string + name string + vnet armnetwork.VirtualNetwork + options *armnetwork.VirtualNetworksClientBeginCreateOrUpdateOptions +} + +type vnetDeleteArgs struct { + rg string + name string + options *armnetwork.VirtualNetworksClientBeginDeleteOptions +} + +func (vnetm *vnetMock) BeginCreateOrUpdate(ctx context.Context, rg, name string, vnet armnetwork.VirtualNetwork, options *armnetwork.VirtualNetworksClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.VirtualNetworksClientCreateOrUpdateResponse], error) { + vnetm.createOrUpdate = append(vnetm.createOrUpdate, vnetCreateOrUpdateArgs{rg, name, vnet, options}) + + vnet.ID = common.ToPtr("vnet-id") + vnet.Name = &name + return makePoller[armnetwork.VirtualNetworksClientCreateOrUpdateResponse]( + &armnetwork.VirtualNetworksClientCreateOrUpdateResponse{ + VirtualNetwork: vnet, + }, + ) +} + +func (vnetm *vnetMock) BeginDelete(ctx context.Context, rg, name string, options *armnetwork.VirtualNetworksClientBeginDeleteOptions) (*runtime.Poller[armnetwork.VirtualNetworksClientDeleteResponse], error) { + vnetm.delete = append(vnetm.delete, vnetDeleteArgs{rg, name, options}) + return makePoller[armnetwork.VirtualNetworksClientDeleteResponse]( + &armnetwork.VirtualNetworksClientDeleteResponse{}, + ) +} + +type subnetMock struct { + createOrUpdate []subnetCreateOrUpdateArgs + delete []subnetDeleteArgs +} + +type subnetCreateOrUpdateArgs struct { + rg string + vnet string + name string + subnet armnetwork.Subnet + options *armnetwork.SubnetsClientBeginCreateOrUpdateOptions +} + +type subnetDeleteArgs struct { + rg string + vnet string + name string + options *armnetwork.SubnetsClientBeginDeleteOptions +} + +func (snm *subnetMock) BeginCreateOrUpdate(ctx context.Context, rg, vnet, name string, sn armnetwork.Subnet, options *armnetwork.SubnetsClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.SubnetsClientCreateOrUpdateResponse], error) { + snm.createOrUpdate = append(snm.createOrUpdate, subnetCreateOrUpdateArgs{rg, vnet, name, sn, options}) + + sn.ID = common.ToPtr("sn-id") + sn.Name = &name + return makePoller[armnetwork.SubnetsClientCreateOrUpdateResponse]( + &armnetwork.SubnetsClientCreateOrUpdateResponse{ + Subnet: sn, + }, + ) +} + +func (snm *subnetMock) BeginDelete(ctx context.Context, rg, vnet, name string, options *armnetwork.SubnetsClientBeginDeleteOptions) (*runtime.Poller[armnetwork.SubnetsClientDeleteResponse], error) { + snm.delete = append(snm.delete, subnetDeleteArgs{rg, vnet, name, options}) + return makePoller[armnetwork.SubnetsClientDeleteResponse]( + &armnetwork.SubnetsClientDeleteResponse{}, + ) +} + +type pipMock struct { + createOrUpdate []pipCreateOrUpdateArgs + delete []pipDeleteArgs +} + +type pipCreateOrUpdateArgs struct { + rg string + name string + pip armnetwork.PublicIPAddress + options *armnetwork.PublicIPAddressesClientBeginCreateOrUpdateOptions +} + +type pipDeleteArgs struct { + rg string + name string + options *armnetwork.PublicIPAddressesClientBeginDeleteOptions +} + +func (pipm *pipMock) BeginCreateOrUpdate(ctx context.Context, rg, name string, pip armnetwork.PublicIPAddress, options *armnetwork.PublicIPAddressesClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.PublicIPAddressesClientCreateOrUpdateResponse], error) { + pipm.createOrUpdate = append(pipm.createOrUpdate, pipCreateOrUpdateArgs{rg, name, pip, options}) + + return makePoller[armnetwork.PublicIPAddressesClientCreateOrUpdateResponse]( + &armnetwork.PublicIPAddressesClientCreateOrUpdateResponse{ + PublicIPAddress: armnetwork.PublicIPAddress{ + Location: pip.Location, + ID: common.ToPtr("pip-id"), + Name: &name, + Properties: &armnetwork.PublicIPAddressPropertiesFormat{ + PublicIPAllocationMethod: pip.Properties.PublicIPAllocationMethod, + IPAddress: common.ToPtr("0.0.0.0"), }, }, }, ) - if err != nil { - return nil, err - } - return p, nil +} + +func (pipm *pipMock) BeginDelete(ctx context.Context, rg, name string, options *armnetwork.PublicIPAddressesClientBeginDeleteOptions) (*runtime.Poller[armnetwork.PublicIPAddressesClientDeleteResponse], error) { + pipm.delete = append(pipm.delete, pipDeleteArgs{rg, name, options}) + return makePoller[armnetwork.PublicIPAddressesClientDeleteResponse]( + &armnetwork.PublicIPAddressesClientDeleteResponse{}, + ) +} + +type sgMock struct { + createOrUpdate []sgCreateOrUpdateArgs + delete []sgDeleteArgs +} + +type sgCreateOrUpdateArgs struct { + rg string + name string + sg armnetwork.SecurityGroup + options *armnetwork.SecurityGroupsClientBeginCreateOrUpdateOptions +} + +type sgDeleteArgs struct { + rg string + name string + options *armnetwork.SecurityGroupsClientBeginDeleteOptions +} + +func (sgm *sgMock) BeginCreateOrUpdate(ctx context.Context, rg, name string, sg armnetwork.SecurityGroup, options *armnetwork.SecurityGroupsClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.SecurityGroupsClientCreateOrUpdateResponse], error) { + sgm.createOrUpdate = append(sgm.createOrUpdate, sgCreateOrUpdateArgs{rg, name, sg, options}) + + sg.ID = common.ToPtr("sg-id") + sg.Name = &name + return makePoller[armnetwork.SecurityGroupsClientCreateOrUpdateResponse]( + &armnetwork.SecurityGroupsClientCreateOrUpdateResponse{ + SecurityGroup: sg, + }, + ) +} + +func (sgm *sgMock) BeginDelete(ctx context.Context, rg, name string, options *armnetwork.SecurityGroupsClientBeginDeleteOptions) (*runtime.Poller[armnetwork.SecurityGroupsClientDeleteResponse], error) { + sgm.delete = append(sgm.delete, sgDeleteArgs{rg, name, options}) + return makePoller[armnetwork.SecurityGroupsClientDeleteResponse]( + &armnetwork.SecurityGroupsClientDeleteResponse{}, + ) +} + +type intfMock struct { + createOrUpdate []intfCreateOrUpdateArgs + delete []intfDeleteArgs +} + +type intfCreateOrUpdateArgs struct { + rg string + name string + intf armnetwork.Interface + options *armnetwork.InterfacesClientBeginCreateOrUpdateOptions +} + +type intfDeleteArgs struct { + rg string + name string + options *armnetwork.InterfacesClientBeginDeleteOptions +} + +func (intfm *intfMock) BeginCreateOrUpdate(ctx context.Context, rg, name string, intf armnetwork.Interface, options *armnetwork.InterfacesClientBeginCreateOrUpdateOptions) (*runtime.Poller[armnetwork.InterfacesClientCreateOrUpdateResponse], error) { + intfm.createOrUpdate = append(intfm.createOrUpdate, intfCreateOrUpdateArgs{rg, name, intf, options}) + + intf.ID = common.ToPtr("intf-id") + intf.Name = &name + return makePoller[armnetwork.InterfacesClientCreateOrUpdateResponse]( + &armnetwork.InterfacesClientCreateOrUpdateResponse{ + Interface: intf, + }, + ) +} + +func (intfm *intfMock) BeginDelete(ctx context.Context, rg, name string, options *armnetwork.InterfacesClientBeginDeleteOptions) (*runtime.Poller[armnetwork.InterfacesClientDeleteResponse], error) { + intfm.delete = append(intfm.delete, intfDeleteArgs{rg, name, options}) + return makePoller[armnetwork.InterfacesClientDeleteResponse]( + &armnetwork.InterfacesClientDeleteResponse{}, + ) +} + +type vmMock struct { + createOrUpdate []vmCreateOrUpdateArgs + delete []vmDeleteArgs +} + +type vmCreateOrUpdateArgs struct { + rg string + name string + vm armcompute.VirtualMachine + options *armcompute.VirtualMachinesClientBeginCreateOrUpdateOptions +} + +type vmDeleteArgs struct { + rg string + name string + options *armcompute.VirtualMachinesClientBeginDeleteOptions +} + +func (vmm *vmMock) BeginCreateOrUpdate(ctx context.Context, rg, name string, vm armcompute.VirtualMachine, options *armcompute.VirtualMachinesClientBeginCreateOrUpdateOptions) (*runtime.Poller[armcompute.VirtualMachinesClientCreateOrUpdateResponse], error) { + vmm.createOrUpdate = append(vmm.createOrUpdate, vmCreateOrUpdateArgs{rg, name, vm, options}) + + vm.ID = common.ToPtr("vm-id") + vm.Name = &name + return makePoller[armcompute.VirtualMachinesClientCreateOrUpdateResponse]( + &armcompute.VirtualMachinesClientCreateOrUpdateResponse{ + VirtualMachine: vm, + }, + ) +} + +func (vmm *vmMock) BeginDelete(ctx context.Context, rg, name string, options *armcompute.VirtualMachinesClientBeginDeleteOptions) (*runtime.Poller[armcompute.VirtualMachinesClientDeleteResponse], error) { + vmm.delete = append(vmm.delete, vmDeleteArgs{rg, name, options}) + return makePoller[armcompute.VirtualMachinesClientDeleteResponse]( + &armcompute.VirtualMachinesClientDeleteResponse{}, + ) +} + +type diskMock struct { + delete []diskDeleteArgs +} + +type diskDeleteArgs struct { + rg string + name string + options *armcompute.DisksClientBeginDeleteOptions +} + +func (diskm *diskMock) BeginDelete(ctx context.Context, rg, name string, options *armcompute.DisksClientBeginDeleteOptions) (*runtime.Poller[armcompute.DisksClientDeleteResponse], error) { + diskm.delete = append(diskm.delete, diskDeleteArgs{rg, name, options}) + return makePoller[armcompute.DisksClientDeleteResponse]( + &armcompute.DisksClientDeleteResponse{}, + ) } diff --git a/pkg/cloud/azure/vm.go b/pkg/cloud/azure/vm.go new file mode 100644 index 0000000000..d61e0544df --- /dev/null +++ b/pkg/cloud/azure/vm.go @@ -0,0 +1,408 @@ +package azure + +import ( + "context" + "fmt" + + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v7" + + "github.com/osbuild/images/internal/common" + "github.com/osbuild/images/pkg/olog" +) + +type VM struct { + ResourceGroup string `json:"resourcegroup"` + VNet string `json:"vnet"` + Subnet string `json:"subnet"` + SG string `json:"sg"` + IPName string `json:"ip-name"` + IPAddress string `json:"ip-address"` + Nic string `json:"nic"` + Name string `json:"name"` + Disk string `json:"disk"` +} + +func (ac Client) CreateVM(ctx context.Context, resourceGroup, image, name, size, username, sshKey string) (*VM, error) { + vm := VM{ + ResourceGroup: resourceGroup, + Name: name, + } + var err error + defer func() { + if err != nil { + err = ac.DestroyVM(ctx, &vm) + if err != nil { + olog.Printf("unable to destroy vm: %s", err.Error()) + } + } + }() + + location, err := ac.GetResourceGroupLocation(ctx, resourceGroup) + if err != nil { + return nil, fmt.Errorf("retrieving resource group location failed: %w", err) + } + + virtualNetwork, err := ac.createVirtualNetwork(ctx, resourceGroup, location, fmt.Sprintf("%s-vnet", vm.Name)) + if err != nil { + return nil, err + } + vm.VNet = common.ValueOrEmpty(virtualNetwork.Name) + + subnet, err := ac.createSubnet(ctx, resourceGroup, common.ValueOrEmpty(virtualNetwork.Name), fmt.Sprintf("%s-subnet", vm.Name)) + if err != nil { + return nil, err + } + vm.Subnet = common.ValueOrEmpty(subnet.Name) + + publicIP, err := ac.createPublicIP(ctx, resourceGroup, location, fmt.Sprintf("%s-ip", vm.Name)) + if err != nil { + return nil, err + } + vm.IPName = common.ValueOrEmpty(publicIP.Name) + vm.IPAddress = common.ValueOrEmpty(publicIP.Properties.IPAddress) + + sg, err := ac.createSG(ctx, resourceGroup, location, fmt.Sprintf("%s-sg", vm.Name)) + if err != nil { + return nil, err + } + vm.SG = common.ValueOrEmpty(sg.Name) + + intf, err := ac.createInterface(ctx, resourceGroup, location, *subnet.ID, *publicIP.ID, *sg.ID, fmt.Sprintf("%s-intf", vm.Name)) + if err != nil { + return nil, err + } + vm.Nic = common.ValueOrEmpty(intf.Name) + + virtualMachine, err := ac.createVM(ctx, resourceGroup, location, image, size, *intf.ID, fmt.Sprintf("%s-disk", vm.Name), vm.Name, username, sshKey) + if err != nil { + return nil, err + } + vm.Name = common.ValueOrEmpty(virtualMachine.Name) + vm.Disk = common.ValueOrEmpty(virtualMachine.Properties.StorageProfile.OSDisk.Name) + return &vm, nil +} + +func (ac Client) DestroyVM(ctx context.Context, vm *VM) error { + err := ac.deleteVirtualMachine(ctx, vm) + if err != nil { + return err + } + err = ac.deleteDisk(ctx, vm) + if err != nil { + return err + } + err = ac.deleteInterface(ctx, vm) + if err != nil { + return err + } + err = ac.deleteSG(ctx, vm) + if err != nil { + return err + } + err = ac.deletePublicIP(ctx, vm) + if err != nil { + return err + } + err = ac.deleteSubnet(ctx, vm) + if err != nil { + return err + } + err = ac.deleteVirtualNetwork(ctx, vm) + if err != nil { + return err + } + return nil +} + +func (ac Client) createVirtualNetwork(ctx context.Context, resourceGroup, location, name string) (*armnetwork.VirtualNetwork, error) { + poller, err := ac.vnets.BeginCreateOrUpdate(ctx, resourceGroup, name, armnetwork.VirtualNetwork{ + Location: &location, + Properties: &armnetwork.VirtualNetworkPropertiesFormat{ + AddressSpace: &armnetwork.AddressSpace{ + AddressPrefixes: []*string{ + common.ToPtr("10.1.0.0/16"), + }, + }, + }, + }, nil) + if err != nil { + return nil, err + } + resp, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return nil, err + } + return &resp.VirtualNetwork, err +} + +func (ac Client) deleteVirtualNetwork(ctx context.Context, vm *VM) error { + if vm.VNet == "" { + return nil + } + poller, err := ac.vnets.BeginDelete(ctx, vm.ResourceGroup, vm.VNet, nil) + if err != nil { + return err + } + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + return nil +} + +func (ac Client) createSubnet(ctx context.Context, resourceGroup, vnet, name string) (*armnetwork.Subnet, error) { + poller, err := ac.subnets.BeginCreateOrUpdate(ctx, resourceGroup, vnet, name, armnetwork.Subnet{ + Properties: &armnetwork.SubnetPropertiesFormat{ + AddressPrefix: common.ToPtr("10.1.10.0/24"), + }, + }, nil) + if err != nil { + return nil, err + } + resp, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return nil, err + } + return &resp.Subnet, nil +} + +func (ac Client) deleteSubnet(ctx context.Context, vm *VM) error { + if vm.Subnet == "" { + return nil + } + poller, err := ac.subnets.BeginDelete(ctx, vm.ResourceGroup, vm.VNet, vm.Subnet, nil) + if err != nil { + return err + } + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + return nil +} + +func (ac Client) createSG(ctx context.Context, resourceGroup, location, name string) (*armnetwork.SecurityGroup, error) { + poller, err := ac.securityGroups.BeginCreateOrUpdate(ctx, resourceGroup, name, armnetwork.SecurityGroup{ + Location: &location, + Properties: &armnetwork.SecurityGroupPropertiesFormat{ + SecurityRules: []*armnetwork.SecurityRule{ + { + Name: common.ToPtr("ssh"), + Properties: &armnetwork.SecurityRulePropertiesFormat{ + SourceAddressPrefix: common.ToPtr("*"), + SourcePortRange: common.ToPtr("*"), + DestinationAddressPrefix: common.ToPtr("*"), + DestinationPortRange: common.ToPtr("22"), + Protocol: common.ToPtr(armnetwork.SecurityRuleProtocolTCP), + Access: common.ToPtr(armnetwork.SecurityRuleAccessAllow), + Priority: common.ToPtr[int32](100), + Description: common.ToPtr("ssh"), + Direction: common.ToPtr(armnetwork.SecurityRuleDirectionInbound), + }, + }, + }, + }, + }, nil) + if err != nil { + return nil, err + } + + resp, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return nil, err + } + return &resp.SecurityGroup, nil +} + +func (ac Client) deleteSG(ctx context.Context, vm *VM) error { + if vm.SG == "" { + return nil + } + poller, err := ac.securityGroups.BeginDelete(ctx, vm.ResourceGroup, vm.SG, nil) + if err != nil { + return err + } + + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + return nil +} + +func (ac Client) createPublicIP(ctx context.Context, resourceGroup, location, name string) (*armnetwork.PublicIPAddress, error) { + poller, err := ac.publicIPs.BeginCreateOrUpdate(ctx, resourceGroup, name, armnetwork.PublicIPAddress{ + Location: common.ToPtr(location), + Properties: &armnetwork.PublicIPAddressPropertiesFormat{ + PublicIPAllocationMethod: common.ToPtr(armnetwork.IPAllocationMethodStatic), + }, + }, nil) + if err != nil { + return nil, err + } + + resp, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return nil, err + } + return &resp.PublicIPAddress, err +} + +func (ac Client) deletePublicIP(ctx context.Context, vm *VM) error { + if vm.IPName == "" { + return nil + } + poller, err := ac.publicIPs.BeginDelete(ctx, vm.ResourceGroup, vm.IPName, nil) + if err != nil { + return err + } + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + return nil +} + +func (ac Client) createInterface(ctx context.Context, resourceGroup, location, subnet, publicIP, securityGroup, name string) (*armnetwork.Interface, error) { + poller, err := ac.interfaces.BeginCreateOrUpdate(ctx, resourceGroup, name, armnetwork.Interface{ + Location: common.ToPtr(location), + Properties: &armnetwork.InterfacePropertiesFormat{ + IPConfigurations: []*armnetwork.InterfaceIPConfiguration{ + { + Name: common.ToPtr("ipConfig"), + Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{ + PrivateIPAllocationMethod: common.ToPtr(armnetwork.IPAllocationMethodDynamic), + Subnet: &armnetwork.Subnet{ + ID: common.ToPtr(subnet), + }, + PublicIPAddress: &armnetwork.PublicIPAddress{ + ID: common.ToPtr(publicIP), + }, + }, + }, + }, + NetworkSecurityGroup: &armnetwork.SecurityGroup{ + ID: common.ToPtr(securityGroup), + }, + }, + }, nil) + if err != nil { + return nil, err + } + + resp, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return nil, err + } + + return &resp.Interface, err +} + +func (ac Client) deleteInterface(ctx context.Context, vm *VM) error { + if vm.Nic == "" { + return nil + } + poller, err := ac.interfaces.BeginDelete(ctx, vm.ResourceGroup, vm.Nic, nil) + if err != nil { + return err + } + + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + + return nil +} + +func (ac Client) createVM(ctx context.Context, resourceGroup, location, image, size, nic, diskName, name, username, sshKey string) (*armcompute.VirtualMachine, error) { + vm := armcompute.VirtualMachine{ + Location: common.ToPtr(location), + Identity: &armcompute.VirtualMachineIdentity{ + Type: common.ToPtr(armcompute.ResourceIdentityTypeNone), + }, + Properties: &armcompute.VirtualMachineProperties{ + StorageProfile: &armcompute.StorageProfile{ + ImageReference: &armcompute.ImageReference{ + ID: common.ToPtr(image), + }, + OSDisk: &armcompute.OSDisk{ + Name: common.ToPtr(diskName), + CreateOption: common.ToPtr(armcompute.DiskCreateOptionTypesFromImage), + Caching: common.ToPtr(armcompute.CachingTypesReadWrite), + ManagedDisk: &armcompute.ManagedDiskParameters{ + StorageAccountType: common.ToPtr(armcompute.StorageAccountTypesStandardLRS), + }, + }, + }, + HardwareProfile: &armcompute.HardwareProfile{ + VMSize: common.ToPtr(armcompute.VirtualMachineSizeTypes(size)), + }, + OSProfile: &armcompute.OSProfile{ + ComputerName: common.ToPtr(name), + AdminUsername: common.ToPtr(username), + LinuxConfiguration: &armcompute.LinuxConfiguration{ + DisablePasswordAuthentication: common.ToPtr(true), + SSH: &armcompute.SSHConfiguration{ + PublicKeys: []*armcompute.SSHPublicKey{ + { + Path: common.ToPtr(fmt.Sprintf("/home/%s/.ssh/authorized_keys", username)), + KeyData: common.ToPtr(sshKey), + }, + }, + }, + }, + }, + NetworkProfile: &armcompute.NetworkProfile{ + NetworkInterfaces: []*armcompute.NetworkInterfaceReference{ + { + ID: common.ToPtr(nic), + }, + }, + }, + }, + } + + poller, err := ac.vms.BeginCreateOrUpdate(ctx, resourceGroup, name, vm, nil) + if err != nil { + return nil, err + } + + resp, err := poller.PollUntilDone(ctx, nil) + if err != nil { + return nil, err + } + + return &resp.VirtualMachine, nil +} + +func (ac Client) deleteVirtualMachine(ctx context.Context, vm *VM) error { + if vm.Name == "" { + return nil + } + poller, err := ac.vms.BeginDelete(ctx, vm.ResourceGroup, vm.Name, nil) + if err != nil { + return err + } + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + return nil +} + +func (ac Client) deleteDisk(ctx context.Context, vm *VM) error { + if vm.Disk == "" { + return nil + } + poller, err := ac.disks.BeginDelete(ctx, vm.ResourceGroup, vm.Disk, nil) + if err != nil { + return err + } + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + return nil +} diff --git a/pkg/cloud/azure/vm_test.go b/pkg/cloud/azure/vm_test.go new file mode 100644 index 0000000000..bfaf435ef0 --- /dev/null +++ b/pkg/cloud/azure/vm_test.go @@ -0,0 +1,229 @@ +package azure_test + +import ( + "context" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v7" + "github.com/stretchr/testify/require" + + "github.com/osbuild/images/internal/common" +) + +func TestLaunchVM(t *testing.T) { + azm := newAZ() + + vm, err := azm.az.CreateVM( + context.Background(), + "rg", + "test-image", + "vm-name", + "size", + "username", + "ssh-key", + ) + require.NoError(t, err) + require.Equal(t, "rg", vm.ResourceGroup) + + require.Equal(t, "vm-name", vm.Name) + require.Len(t, azm.vmm.createOrUpdate, 1) + require.Equal(t, + vmCreateOrUpdateArgs{ + name: "vm-name", + rg: "rg", + vm: armcompute.VirtualMachine{ + Location: common.ToPtr("test-universe"), + Identity: &armcompute.VirtualMachineIdentity{ + Type: common.ToPtr(armcompute.ResourceIdentityTypeNone), + }, + Properties: &armcompute.VirtualMachineProperties{ + StorageProfile: &armcompute.StorageProfile{ + ImageReference: &armcompute.ImageReference{ + ID: common.ToPtr("test-image"), + }, + OSDisk: &armcompute.OSDisk{ + Name: &vm.Disk, + CreateOption: common.ToPtr(armcompute.DiskCreateOptionTypesFromImage), + Caching: common.ToPtr(armcompute.CachingTypesReadWrite), + ManagedDisk: &armcompute.ManagedDiskParameters{ + StorageAccountType: common.ToPtr(armcompute.StorageAccountTypesStandardLRS), + }, + }, + }, + HardwareProfile: &armcompute.HardwareProfile{ + VMSize: common.ToPtr(armcompute.VirtualMachineSizeTypes("size")), + }, + OSProfile: &armcompute.OSProfile{ + ComputerName: common.ToPtr(vm.Name), + AdminUsername: common.ToPtr("username"), + LinuxConfiguration: &armcompute.LinuxConfiguration{ + DisablePasswordAuthentication: common.ToPtr(true), + SSH: &armcompute.SSHConfiguration{ + PublicKeys: []*armcompute.SSHPublicKey{ + { + Path: common.ToPtr("/home/username/.ssh/authorized_keys"), + KeyData: common.ToPtr("ssh-key"), + }, + }, + }, + }, + }, + NetworkProfile: &armcompute.NetworkProfile{ + NetworkInterfaces: []*armcompute.NetworkInterfaceReference{ + { + ID: common.ToPtr("intf-id"), + }, + }, + }, + }, + }, + }, azm.vmm.createOrUpdate[0]) + + require.Equal(t, "vm-name-intf", vm.Nic) + require.Len(t, azm.intfm.createOrUpdate, 1) + require.Equal(t, intfCreateOrUpdateArgs{ + rg: "rg", + name: vm.Nic, + intf: armnetwork.Interface{ + Location: common.ToPtr("test-universe"), + Properties: &armnetwork.InterfacePropertiesFormat{ + IPConfigurations: []*armnetwork.InterfaceIPConfiguration{ + { + Name: common.ToPtr("ipConfig"), + Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{ + PrivateIPAllocationMethod: common.ToPtr(armnetwork.IPAllocationMethodDynamic), + Subnet: &armnetwork.Subnet{ + ID: common.ToPtr("sn-id"), + }, + PublicIPAddress: &armnetwork.PublicIPAddress{ + ID: common.ToPtr("pip-id"), + }, + }, + }, + }, + NetworkSecurityGroup: &armnetwork.SecurityGroup{ + ID: common.ToPtr("sg-id"), + }, + }, + }, + }, azm.intfm.createOrUpdate[0]) + + require.Equal(t, "vm-name-sg", vm.SG) + require.Len(t, azm.sgm.createOrUpdate, 1) + require.Equal(t, sgCreateOrUpdateArgs{ + rg: "rg", + name: vm.SG, + sg: armnetwork.SecurityGroup{ + Location: common.ToPtr("test-universe"), + Properties: &armnetwork.SecurityGroupPropertiesFormat{ + SecurityRules: []*armnetwork.SecurityRule{ + { + Name: common.ToPtr("ssh"), + Properties: &armnetwork.SecurityRulePropertiesFormat{ + SourceAddressPrefix: common.ToPtr("*"), + SourcePortRange: common.ToPtr("*"), + DestinationAddressPrefix: common.ToPtr("*"), + DestinationPortRange: common.ToPtr("22"), + Protocol: common.ToPtr(armnetwork.SecurityRuleProtocolTCP), + Access: common.ToPtr(armnetwork.SecurityRuleAccessAllow), + Priority: common.ToPtr[int32](100), + Description: common.ToPtr("ssh"), + Direction: common.ToPtr(armnetwork.SecurityRuleDirectionInbound), + }, + }, + }, + }, + }, + }, azm.sgm.createOrUpdate[0]) + + require.Equal(t, "vm-name-ip", vm.IPName) + require.Equal(t, "0.0.0.0", vm.IPAddress) + require.Len(t, azm.pipm.createOrUpdate, 1) + require.Equal(t, pipCreateOrUpdateArgs{ + rg: "rg", + name: vm.IPName, + pip: armnetwork.PublicIPAddress{ + Location: common.ToPtr("test-universe"), + Properties: &armnetwork.PublicIPAddressPropertiesFormat{ + PublicIPAllocationMethod: common.ToPtr(armnetwork.IPAllocationMethodStatic), + }, + }, + }, azm.pipm.createOrUpdate[0]) + + require.Equal(t, "vm-name-subnet", vm.Subnet) + require.Len(t, azm.snm.createOrUpdate, 1) + require.Equal(t, subnetCreateOrUpdateArgs{ + rg: "rg", + name: vm.Subnet, + vnet: vm.VNet, + subnet: armnetwork.Subnet{ + Properties: &armnetwork.SubnetPropertiesFormat{ + AddressPrefix: common.ToPtr("10.1.10.0/24"), + }, + }, + }, azm.snm.createOrUpdate[0]) + + require.Equal(t, "vm-name-vnet", vm.VNet) + require.Len(t, azm.vnetm.createOrUpdate, 1) + require.Equal(t, vnetCreateOrUpdateArgs{ + rg: "rg", + name: vm.VNet, + vnet: armnetwork.VirtualNetwork{ + Location: common.ToPtr("test-universe"), + Properties: &armnetwork.VirtualNetworkPropertiesFormat{ + AddressSpace: &armnetwork.AddressSpace{ + AddressPrefixes: []*string{ + common.ToPtr("10.1.0.0/16"), + }, + }, + }, + }, + }, azm.vnetm.createOrUpdate[0]) + + require.Equal(t, "vm-name-disk", vm.Disk) + + require.NoError(t, azm.az.DestroyVM(context.Background(), vm)) + require.Len(t, azm.vmm.delete, 1) + require.Equal(t, vmDeleteArgs{ + rg: "rg", + name: vm.Name, + }, azm.vmm.delete[0]) + + require.Len(t, azm.diskm.delete, 1) + require.Equal(t, diskDeleteArgs{ + rg: "rg", + name: vm.Disk, + }, azm.diskm.delete[0]) + + require.Len(t, azm.intfm.delete, 1) + require.Equal(t, intfDeleteArgs{ + rg: "rg", + name: vm.Nic, + }, azm.intfm.delete[0]) + + require.Len(t, azm.sgm.delete, 1) + require.Equal(t, sgDeleteArgs{ + rg: "rg", + name: vm.SG, + }, azm.sgm.delete[0]) + + require.Len(t, azm.pipm.delete, 1) + require.Equal(t, pipDeleteArgs{ + rg: "rg", + name: vm.IPName, + }, azm.pipm.delete[0]) + + require.Len(t, azm.snm.delete, 1) + require.Equal(t, subnetDeleteArgs{ + rg: "rg", + name: vm.Subnet, + vnet: vm.VNet, + }, azm.snm.delete[0]) + + require.Len(t, azm.vnetm.delete, 1) + require.Equal(t, vnetDeleteArgs{ + rg: "rg", + name: vm.VNet, + }, azm.vnetm.delete[0]) +} diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-ami-all_customizations b/test/data/manifest-checksums/rhel_8.4-aarch64-ami-all_customizations index 36dd1ef64f..5e73fb55dc 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-ami-all_customizations +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-ami-all_customizations @@ -1 +1 @@ -852a2f2684318371abbdcd068ca60d2cf33955ab +9e81c8e31d5dae6a49e032d0fcd11466874d5a13 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-ami-empty_rhel b/test/data/manifest-checksums/rhel_8.4-aarch64-ami-empty_rhel index a85587e6e8..fb451ecec7 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-ami-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-ami-empty_rhel @@ -1 +1 @@ -bac4401ef0cc0480929a6ae5a5eb6041b75260d6 +8702c6f013b6c1dd6cb69438c306b212dc6eac1e diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-ami-partitioning_lvm_noswap b/test/data/manifest-checksums/rhel_8.4-aarch64-ami-partitioning_lvm_noswap index d41f6f3570..fb2eee62dc 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-ami-partitioning_lvm_noswap +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-ami-partitioning_lvm_noswap @@ -1 +1 @@ -bb3ea816036201cd4081dd96721ef595edb8cf25 +8da14816db9499adda241e467763a385ee33247a diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-ec2-empty_rhel b/test/data/manifest-checksums/rhel_8.4-aarch64-ec2-empty_rhel index c59bf1f18c..2ff826b659 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-ec2-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-ec2-empty_rhel @@ -1 +1 @@ -c34086c4ea9a172ba85704df524b80400a47068e +270b8248c635e8e7576c4cc05c480d1be2e45304 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-edge_ostree_pull_user b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-edge_ostree_pull_user index e40ea8c835..8d6454bdfd 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-edge_ostree_pull_user +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-edge_ostree_pull_user @@ -1 +1 @@ -dc9632afd9eb9e842b17be7c183607a81d293721 +db308276949d8a6d4bf3329758d6e1c1eec5b695 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-embed_containers b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-embed_containers index b9fb779546..d3c71becf9 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-embed_containers +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-embed_containers @@ -1 +1 @@ -295581d5ad7fb22acc1df0878730812399bbc594 +d4308b1aa877149035b29344d2db1a8929308b68 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-ostree b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-ostree index de59199f06..d853d43438 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-ostree +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_commit-ostree @@ -1 +1 @@ -6e4966747ed19323ccac844294c3401942552804 +42d71c8f155adfa9c9e125ad9a956fc89b0486ca diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-embed_containers_2 b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-embed_containers_2 index a644d20a78..cff806fb4d 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-embed_containers_2 +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-embed_containers_2 @@ -1 +1 @@ -8376ca646f4aadda03aad08417cccd0592fd85b6 +d4e125d1538a0efbf0a0a925fcb8ec298ffd5de2 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-empty_rhel b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-empty_rhel index 8a55e7a833..97a5c58bca 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-empty_rhel @@ -1 +1 @@ -954c070fd41985eb82e7b85d52091b2df61a8fd9 +5f755b3f73f62c702ed2f5b0787318b85ea0cf44 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-ostree b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-ostree index 74293da8e6..eb019093c8 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-ostree +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_container-ostree @@ -1 +1 @@ -3f14540dc12cbfe1d1bd4c08cd1528f250d82bad +7e406848bb6a6a0e8c22bd7e3dde987e18571818 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_installer-edge_ostree_pull_empty b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_installer-edge_ostree_pull_empty index 7a33884d99..67c2f504e1 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_installer-edge_ostree_pull_empty +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_installer-edge_ostree_pull_empty @@ -1 +1 @@ -29587cb84e540b6af789bf93c4fae500df6d6ea3 +c674bb67b9e255fed9aabb3e0e15062bca55b1f8 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_installer-unattended_iso_edge b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_installer-unattended_iso_edge index 20d5d2c043..26bfdddc27 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-edge_installer-unattended_iso_edge +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-edge_installer-unattended_iso_edge @@ -1 +1 @@ -133d889a1c71d09d104caa0d6d9ff99b4a7658a5 +203474acac249c4d68fd6cdf6b6853fba9e582c0 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-image_installer-empty_rhel b/test/data/manifest-checksums/rhel_8.4-aarch64-image_installer-empty_rhel index e52a5eafd5..9528e1931a 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-image_installer-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-image_installer-empty_rhel @@ -1 +1 @@ -2c74e9cb93714af6dcfdf5994f22e6fafa8b96f0 +48716a943e13ea91283aeab7adcb2cbc5d4fa938 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-image_installer-unattended_iso b/test/data/manifest-checksums/rhel_8.4-aarch64-image_installer-unattended_iso index bd0b3cb643..fedd3d2bf8 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-image_installer-unattended_iso +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-image_installer-unattended_iso @@ -1 +1 @@ -5b89af7fec693f3940ecd39dc61fb7e3385fee0a +79224e2c66198582386fab6e52d10232fcdc154d diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-minimal_raw-empty_rhel b/test/data/manifest-checksums/rhel_8.4-aarch64-minimal_raw-empty_rhel index e59dba1086..35bf2c5a44 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-minimal_raw-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-minimal_raw-empty_rhel @@ -1 +1 @@ -274f3007b933f043a1a413f0b727e0930379275e +b47cfe5dca0f1471008cf3a45e7678a8ee953e93 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-minimal_raw-firewall b/test/data/manifest-checksums/rhel_8.4-aarch64-minimal_raw-firewall index 07c5fb04e1..b0e9c22bbd 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-minimal_raw-firewall +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-minimal_raw-firewall @@ -1 +1 @@ -062cfceca828442cac13d1cf930cd673d00e6ea7 +4d34e3114dba680bbd32de73e002417485779db8 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-openstack-empty_rhel b/test/data/manifest-checksums/rhel_8.4-aarch64-openstack-empty_rhel index 17deaad5c6..9b3ba4d097 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-openstack-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-openstack-empty_rhel @@ -1 +1 @@ -dc5ed645ff2d0d5a812dbfd1f133f8947befec6e +5adab41a3fc749ae5315c1944fd0654ea60b635e diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-qcow2-empty_rhel b/test/data/manifest-checksums/rhel_8.4-aarch64-qcow2-empty_rhel index c008b7cf12..ca4023dda4 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-qcow2-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-qcow2-empty_rhel @@ -1 +1 @@ -02ab16ce6ba7f025a9b003fc236804c4d081f2eb +d377c98c8ea2c9c3dc191b8f6b60d40d60c2004b diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-qcow2-oscap_generic b/test/data/manifest-checksums/rhel_8.4-aarch64-qcow2-oscap_generic index ca85e51ed1..84354ba307 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-qcow2-oscap_generic +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-qcow2-oscap_generic @@ -1 +1 @@ -734fdd7f639d2d0ccf746cdbedd7f6b25c571c0c +99de7371375f309b8341d793ac729ada739b1da5 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-tar-empty_rhel b/test/data/manifest-checksums/rhel_8.4-aarch64-tar-empty_rhel index e334accf04..3826d0ea57 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-tar-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-tar-empty_rhel @@ -1 +1 @@ -f8515c112030174242b40a47af887818f4527679 +6c9e39d6430711ba58cf745c14f9aab4ef9b10f6 diff --git a/test/data/manifest-checksums/rhel_8.4-aarch64-wsl-empty_rhel b/test/data/manifest-checksums/rhel_8.4-aarch64-wsl-empty_rhel index 4ad751a00a..cc7310c2e9 100644 --- a/test/data/manifest-checksums/rhel_8.4-aarch64-wsl-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-aarch64-wsl-empty_rhel @@ -1 +1 @@ -c1d39c2790231f46a1d82943e0edb682a460e24e +357d31fce1a68a3f0255244f799d1ce042de7d92 diff --git a/test/data/manifest-checksums/rhel_8.4-ppc64le-qcow2-empty_rhel b/test/data/manifest-checksums/rhel_8.4-ppc64le-qcow2-empty_rhel index e40ad0f3af..123cc1bfa1 100644 --- a/test/data/manifest-checksums/rhel_8.4-ppc64le-qcow2-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-ppc64le-qcow2-empty_rhel @@ -1 +1 @@ -b85a45f2ef186bdf8cf8fa9e7e0d4e918ca3dc85 +98efd381ec1f7930079404eebf807cb78d5fac7e diff --git a/test/data/manifest-checksums/rhel_8.4-ppc64le-qcow2-oscap_generic b/test/data/manifest-checksums/rhel_8.4-ppc64le-qcow2-oscap_generic index f9e8f56da2..729bee24ba 100644 --- a/test/data/manifest-checksums/rhel_8.4-ppc64le-qcow2-oscap_generic +++ b/test/data/manifest-checksums/rhel_8.4-ppc64le-qcow2-oscap_generic @@ -1 +1 @@ -452388b5108b03987d0ed79989a9ba6ea8ae0430 +6016248107acb6ab29a04631bd73417d1598308d diff --git a/test/data/manifest-checksums/rhel_8.4-ppc64le-tar-empty_rhel b/test/data/manifest-checksums/rhel_8.4-ppc64le-tar-empty_rhel index b74a2bd47c..c2e8470aed 100644 --- a/test/data/manifest-checksums/rhel_8.4-ppc64le-tar-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-ppc64le-tar-empty_rhel @@ -1 +1 @@ -f6ae0d300e7ad89510e45249579c626f7ef2e61e +f2ffb3ee3dae052be1f7b16313765aaeab1f5141 diff --git a/test/data/manifest-checksums/rhel_8.4-s390x-qcow2-empty_rhel b/test/data/manifest-checksums/rhel_8.4-s390x-qcow2-empty_rhel index 1fba015c52..ee62210bcd 100644 --- a/test/data/manifest-checksums/rhel_8.4-s390x-qcow2-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-s390x-qcow2-empty_rhel @@ -1 +1 @@ -95520cde82175ce98d58f19a9a73235468b06be2 +dbad783f136cc264946dc3b052261771767f28b9 diff --git a/test/data/manifest-checksums/rhel_8.4-s390x-qcow2-oscap_generic b/test/data/manifest-checksums/rhel_8.4-s390x-qcow2-oscap_generic index 32abd4c403..ee318c7c6f 100644 --- a/test/data/manifest-checksums/rhel_8.4-s390x-qcow2-oscap_generic +++ b/test/data/manifest-checksums/rhel_8.4-s390x-qcow2-oscap_generic @@ -1 +1 @@ -e2fe9f1dfeb9d48f799de92e8c7265e3b81eb7ed +7c80dd76393a7d0092523e7d732a0ce1d135e572 diff --git a/test/data/manifest-checksums/rhel_8.4-s390x-tar-empty_rhel b/test/data/manifest-checksums/rhel_8.4-s390x-tar-empty_rhel index ed33a32a15..3e56a21932 100644 --- a/test/data/manifest-checksums/rhel_8.4-s390x-tar-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-s390x-tar-empty_rhel @@ -1 +1 @@ -d80435a0e845ae6ba430251727192db130751e86 +438904c7c7d54d0eb3a2ffd46d6318cafaa310b4 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-all_customizations b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-all_customizations index ae1d616cc1..3483d2371d 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-all_customizations +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-all_customizations @@ -1 +1 @@ -cbda6efceb69b6a70713f1ac8a1d07a71a5e7c54 +f69205b2ffcb9a1afa633052d854c3b36a6d1e9b diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-empty_rhel index 974313b0ea..616ceef900 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-empty_rhel @@ -1 +1 @@ -a53ac7958e629a703d6519ab0f1407423ebced34 +f5af0eb285faad78a9879d2736ec4f82e6375f4d diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_lvm_noswap b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_lvm_noswap index a07be1855b..d37753ec81 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_lvm_noswap +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_lvm_noswap @@ -1 +1 @@ -5e718bd5afb1682fa517370990ebff293fc2e1d1 +806362d6e0c903df99941c436a5683d36960bf28 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_lvm_r8 b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_lvm_r8 index e8a5bd38b1..4abdd35032 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_lvm_r8 +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_lvm_r8 @@ -1 +1 @@ -e5335900aa3f796836640a1a8a4145d0471cf8e4 +1ac4af845d00c903bc11871edd0fe2642db4ad2c diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_plain_r8 b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_plain_r8 index 4ba83fc9e6..0d3a7a0d4f 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_plain_r8 +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-ami-partitioning_plain_r8 @@ -1 +1 @@ -1cd123f01d7acc9a47c6246bf461f7ed60355bd8 +37f442e7c16aac71bdf5ba9d333a3072e9a72fd8 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-azure_rhui-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-azure_rhui-empty_rhel index 29fc59682d..80be6c1470 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-azure_rhui-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-azure_rhui-empty_rhel @@ -1 +1 @@ -00ed905f1a7c6fcbfd3fc1f7654a8011d4ad3a4e +85cb4d37d6cffe4e335661a162d9134534b293d5 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-azure_sap_rhui-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-azure_sap_rhui-empty_rhel index 4bd31fc24b..09419ab687 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-azure_sap_rhui-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-azure_sap_rhui-empty_rhel @@ -1 +1 @@ -d32860c5ca62a492280c18072494f904657dc710 +a2e8e48f341bf71a601fb6570124bdb21386ef6c diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-ec2-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-ec2-empty_rhel index 1a22a5dc57..47b72cec08 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-ec2-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-ec2-empty_rhel @@ -1 +1 @@ -90211504f4d52acc9c09a5d3cc214dd437899e0f +70c08c518f63c6e1d6cf845078a7ddcbb0d6c67e diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-ec2_ha-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-ec2_ha-empty_rhel index bf9bc21674..2776836a69 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-ec2_ha-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-ec2_ha-empty_rhel @@ -1 +1 @@ -e38726fbc31fbe3a7191ba31c58c6214aebdc8a4 +1cd23204e3600ad6dcf829c00edf82836bf77e87 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-ec2_sap-disable_lm_sensors b/test/data/manifest-checksums/rhel_8.4-x86_64-ec2_sap-disable_lm_sensors index 9a4a1357e1..ea7c32d04f 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-ec2_sap-disable_lm_sensors +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-ec2_sap-disable_lm_sensors @@ -1 +1 @@ -6ff41440d941709e6ee51e93ffeeb63b36824dd0 +09c96aec4733603fbbd26dd12110eb2faa6fb0e1 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-edge_ostree_pull_user b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-edge_ostree_pull_user index e7e7d9811d..73d98df288 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-edge_ostree_pull_user +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-edge_ostree_pull_user @@ -1 +1 @@ -5ddc5182bdb5ab1d0d8618a519c637acbafb7465 +0f9f99da09d6ea8e61ae94f374c92426a56f0dde diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-embed_containers b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-embed_containers index efe53c4edd..006136aa9f 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-embed_containers +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-embed_containers @@ -1 +1 @@ -c986aadcda1d4567410df06a65d9a608001fe5e7 +76f01a2833ae71939b2e47b2a6b34d61b267c8d4 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-ostree b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-ostree index 60e08057f5..baaf318427 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-ostree +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_commit-ostree @@ -1 +1 @@ -c8a2d5ae103d228d60579a10f263792867e8f37c +aed755c84694fac846987c626f5f40066b8a3dc6 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-embed_containers_2 b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-embed_containers_2 index 0942518f32..0264782518 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-embed_containers_2 +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-embed_containers_2 @@ -1 +1 @@ -4e52d9e3cf86680f531b1eb7c3f19ae3f5c0ba74 +df852cb036a2e3b6be05804f25d5b62a44257c15 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-empty_rhel index 89a5cacec4..56fe73504b 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-empty_rhel @@ -1 +1 @@ -070ac33d2263c4b5ab4ef31fa62d19cf17b98ac3 +4bfc3eec083e43f1fe691babec12c885ef23daea diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-ostree b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-ostree index 692916daa9..454a27650c 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-ostree +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_container-ostree @@ -1 +1 @@ -9bfa1082e8aa3ed9bfb16c778188d5770921ef6a +22441725164d403f7fb34a459bd3b35402a0aa4a diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_installer-edge_ostree_pull_empty b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_installer-edge_ostree_pull_empty index a03aef63e4..a4c94b7d63 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_installer-edge_ostree_pull_empty +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_installer-edge_ostree_pull_empty @@ -1 +1 @@ -9db725c8d7002e0cd277b9049bdbd6c45555f685 +c41704a95de16af450710bcefbb95d435f15c5c2 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_installer-unattended_iso_edge b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_installer-unattended_iso_edge index d8225d6249..871dbbd52e 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-edge_installer-unattended_iso_edge +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-edge_installer-unattended_iso_edge @@ -1 +1 @@ -5dcaa2a147a13dfe2f41f8cf8491900b71e8e398 +5c9b906d65c5c5aa9896216e5c67892665b12160 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-gce-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-gce-empty_rhel index 06f3380335..7094756ab0 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-gce-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-gce-empty_rhel @@ -1 +1 @@ -02c1ff3be836689fbeaf83a2c1cd8aaac232dbd3 +f413fbd8cd306ef5a87a0c3a4c8cb240c9de9247 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-gce_rhui-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-gce_rhui-empty_rhel index b6de0b06b7..da80bec3b4 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-gce_rhui-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-gce_rhui-empty_rhel @@ -1 +1 @@ -35c7ff1c2cbc717d8841f64a8607ae94a7322bf3 +6c3577e13dc78869ade06d7f871c06196adb6972 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-image_installer-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-image_installer-empty_rhel index 25e3ca88ae..113faf960b 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-image_installer-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-image_installer-empty_rhel @@ -1 +1 @@ -40e96fb226d0fb5969d772be8b751ce6e21d6362 +2e1eec9bbab5813111c21ed30ac65df445bf290f diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-image_installer-unattended_iso b/test/data/manifest-checksums/rhel_8.4-x86_64-image_installer-unattended_iso index 1868d6b2eb..9626eafa6d 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-image_installer-unattended_iso +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-image_installer-unattended_iso @@ -1 +1 @@ -b7acef7d80ac77dd35d6cacc0efbfca534e86628 +dc4c971d926d91cb6ca36480992bfd17a414f08a diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-minimal_raw-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-minimal_raw-empty_rhel index 766c8fd708..50e421b607 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-minimal_raw-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-minimal_raw-empty_rhel @@ -1 +1 @@ -6bc94c321b7b404c50462d8af98a913aa46f0406 +64bd877c9540568658ed1e88d22eafd4d18efc1e diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-minimal_raw-firewall b/test/data/manifest-checksums/rhel_8.4-x86_64-minimal_raw-firewall index 9ff840fbb3..9283245d84 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-minimal_raw-firewall +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-minimal_raw-firewall @@ -1 +1 @@ -6400a57703410e1ad4d4ddba9a9797a8ba6b5cb0 +a5f1e900435c93ac7089910e5d1bf3b948481e7d diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-oci-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-oci-empty_rhel index 454d5041e4..ebda6b0314 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-oci-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-oci-empty_rhel @@ -1 +1 @@ -e66d8e6b5a27f6559d22977043ed31ad22d196c2 +d0ccb3bec29d81687799432cf11c8b660fae6936 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-openstack-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-openstack-empty_rhel index 652d9badfe..7fcef20557 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-openstack-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-openstack-empty_rhel @@ -1 +1 @@ -112919d7efc8ff137d03c71a172ef1958d8aae68 +c348e24bd00eee01bbb4ec074a42424545e5fdf8 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-ova-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-ova-empty_rhel index a3e12eeef7..d32dcef3f8 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-ova-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-ova-empty_rhel @@ -1 +1 @@ -10d4561919a41bdee8abb97510539260e6233cff +e84162cc25e1f1f57627d81e50c037e5d51dfef8 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-qcow2-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-qcow2-empty_rhel index ca9336f8aa..8d112a3ebd 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-qcow2-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-qcow2-empty_rhel @@ -1 +1 @@ -9da03cac6f767f2d8b897c7cd8ccff4f8aba42bb +1c8e28e7ee511856b76336f8ca55d1c161a02d9f diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-qcow2-oscap_generic b/test/data/manifest-checksums/rhel_8.4-x86_64-qcow2-oscap_generic index 569c781ef8..8dac84217b 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-qcow2-oscap_generic +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-qcow2-oscap_generic @@ -1 +1 @@ -e43c2adccc5514e68568545e726962476a14922e +49e6165229c450a75903f29d560a8ab0d7b1ede5 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-tar-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-tar-empty_rhel index 5b88e29f95..f70acca48f 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-tar-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-tar-empty_rhel @@ -1 +1 @@ -eae4a36e399fc656d9438f57f8910d2bb56f195c +10fc1a3e4e0ee11d19ae5e8a8080915985d55e51 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-vhd-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-vhd-empty_rhel index 1cbdf32e2b..7124223a64 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-vhd-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-vhd-empty_rhel @@ -1 +1 @@ -520c2355d7c2d4700c9b3d454eda8c17971169c4 +b9aa71c7cd86dd94b046a0f76b7202cfb79797e0 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-vmdk-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-vmdk-empty_rhel index e5351d3a9c..a463000d69 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-vmdk-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-vmdk-empty_rhel @@ -1 +1 @@ -ab4edec4d21ba1f1123b00bfb1f90831a53a7e0a +e0ffe8e242664c91bc785b9631159f9297bd6bf7 diff --git a/test/data/manifest-checksums/rhel_8.4-x86_64-wsl-empty_rhel b/test/data/manifest-checksums/rhel_8.4-x86_64-wsl-empty_rhel index f710705532..edf39f415f 100644 --- a/test/data/manifest-checksums/rhel_8.4-x86_64-wsl-empty_rhel +++ b/test/data/manifest-checksums/rhel_8.4-x86_64-wsl-empty_rhel @@ -1 +1 @@ -b60af8e71d7f429a26743884ede01fa0f5762ad0 +1d889fbcc3c9c266598b2b6232c41b9c63a2e38b diff --git a/test/data/repositories/rhel-8.4.json b/test/data/repositories/rhel-8.4.json index 74ddb5f463..7ecb882480 100644 --- a/test/data/repositories/rhel-8.4.json +++ b/test/data/repositories/rhel-8.4.json @@ -2,7 +2,7 @@ "x86_64": [ { "name": "baseos", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-baseos-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-baseos-e8.4-20250825", "gpgkeys": [ "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwEIACACGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSuBJPAAK\nCRAZni+R/UMdUfIkD/9m3HWv07uJG26R3KBexTo2FFu3rmZs+m2nfW8R3dBX+k0o\nAOFpgJCsNgKwU81LOPrkMN19G0+Yn/ZTCDD7cIQ7dhYuDyEX97xh4une/EhnnRuh\nASzR+1xYbj/HcYZIL9kbslgpebMn+AhxbUTQF/mziug3hLidR9Bzvygq0Q09E11c\nOZL4BU6J2HqxL+9m2F+tnLdfhL7MsAq9nbmWAOpkbGefc5SXBSq0sWfwoes3X3yD\nQ8B5Xqr9AxABU7oUB+wRqvY69ZCxi/BhuuJCUxY89ZmwXfkVxeHl1tYfROUwOnJO\nGYSbI/o41KBK4DkIiDcT7QqvqvCyudnxZdBjL2QU6OrIJvWmKs319qSF9m3mXRSt\nZzWtB89Pj5LZ6cdtuHvW9GO4qSoBLmAfB313pGkbgi1DE6tqCLHlA0yQ8zv99OWV\ncMDGmS7tVTZqfX1xQJ0N3bNORQNtikJC3G+zBCJzIeZleeDlMDQcww00yWU1oE7/\nTo2UmykMGc7o9iggFWR2g0PIcKsA/SXdRKWPqCHG2uKHBvdRTQGupdXQ1sbV+AHw\nycyA/9H/mp/NUSNM2cqnBDcZ6GhlHt59zWtEveiuU5fpTbp4GVcFXbW8jStj8j8z\n1HI3cywZO8+YNPzqyx0JWsidXGkfzkPHyS4jTG84lfu2JG8m/nqLnRSeKpl20Q==\n=79bX\n-----END PGP PUBLIC KEY BLOCK-----", "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----" @@ -10,7 +10,7 @@ }, { "name": "appstream", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-appstream-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-appstream-e8.4-20250825", "gpgkeys": [ "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwEIACACGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSuBJPAAK\nCRAZni+R/UMdUfIkD/9m3HWv07uJG26R3KBexTo2FFu3rmZs+m2nfW8R3dBX+k0o\nAOFpgJCsNgKwU81LOPrkMN19G0+Yn/ZTCDD7cIQ7dhYuDyEX97xh4une/EhnnRuh\nASzR+1xYbj/HcYZIL9kbslgpebMn+AhxbUTQF/mziug3hLidR9Bzvygq0Q09E11c\nOZL4BU6J2HqxL+9m2F+tnLdfhL7MsAq9nbmWAOpkbGefc5SXBSq0sWfwoes3X3yD\nQ8B5Xqr9AxABU7oUB+wRqvY69ZCxi/BhuuJCUxY89ZmwXfkVxeHl1tYfROUwOnJO\nGYSbI/o41KBK4DkIiDcT7QqvqvCyudnxZdBjL2QU6OrIJvWmKs319qSF9m3mXRSt\nZzWtB89Pj5LZ6cdtuHvW9GO4qSoBLmAfB313pGkbgi1DE6tqCLHlA0yQ8zv99OWV\ncMDGmS7tVTZqfX1xQJ0N3bNORQNtikJC3G+zBCJzIeZleeDlMDQcww00yWU1oE7/\nTo2UmykMGc7o9iggFWR2g0PIcKsA/SXdRKWPqCHG2uKHBvdRTQGupdXQ1sbV+AHw\nycyA/9H/mp/NUSNM2cqnBDcZ6GhlHt59zWtEveiuU5fpTbp4GVcFXbW8jStj8j8z\n1HI3cywZO8+YNPzqyx0JWsidXGkfzkPHyS4jTG84lfu2JG8m/nqLnRSeKpl20Q==\n=79bX\n-----END PGP PUBLIC KEY BLOCK-----", "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFsy23UBEACUKSphFEIEvNpy68VeW4Dt6qv+mU6am9a2AAl10JANLj1oqWX+\noYk3en1S6cVe2qehSL5DGVa3HMUZkP3dtbD4SgzXzxPodebPcr4+0QNWigkUisri\nXGL5SCEcOP30zDhZvg+4mpO2jMi7Kc1DLPzBBkgppcX91wa0L1pQzBcvYMPyV/Dh\nKbQHR75WdkP6OA2JXdfC94nxYq+2e0iPqC1hCP3Elh+YnSkOkrawDPmoB1g4+ft/\nxsiVGVy/W0ekXmgvYEHt6si6Y8NwXgnTMqxeSXQ9YUgVIbTpsxHQKGy76T5lMlWX\n4LCOmEVomBJg1SqF6yi9Vu8TeNThaDqT4/DddYInd0OO69s0kGIXalVgGYiW2HOD\nx2q5R1VGCoJxXomz+EbOXY+HpKPOHAjU0DB9MxbU3S248LQ69nIB5uxysy0PSco1\nsdZ8sxRNQ9Dw6on0Nowx5m6Thefzs5iK3dnPGBqHTT43DHbnWc2scjQFG+eZhe98\nEll/kb6vpBoY4bG9/wCG9qu7jj9Z+BceCNKeHllbezVLCU/Hswivr7h2dnaEFvPD\nO4GqiWiwOF06XaBMVgxA8p2HRw0KtXqOpZk+o+sUvdPjsBw42BB96A1yFX4jgFNA\nPyZYnEUdP6OOv9HSjnl7k/iEkvHq/jGYMMojixlvXpGXhnt5jNyc4GSUJQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChhdXhpbGlhcnkga2V5KSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjkEEwECACMFAlsy23UCGwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIX\ngAAKCRD3b2bD1AgnknqOD/9fB2ASuG2aJIiap4kK58R+RmOVM4qgclAnaG57+vjI\nnKvyfV3NH/keplGNRxwqHekfPCqvkpABwhdGEXIE8ILqnPewIMr6PZNZWNJynZ9i\neSMzVuCG7jDoGyQ5/6B0f6xeBtTeBDiRl7+Alehet1twuGL1BJUYG0QuLgcEzkaE\n/gkuumeVcazLzz7L12D22nMk66GxmgXfqS5zcbqOAuZwaA6VgSEgFdV2X2JU79zS\nBQJXv7NKc+nDXFG7M7EHjY3Rma3HXkDbkT8bzh9tJV7Z7TlpT829pStWQyoxKCVq\nsEX8WsSapTKA3P9YkYCwLShgZu4HKRFvHMaIasSIZWzLu+RZH/4yyHOhj0QB7XMY\neHQ6fGSbtJ+K6SrpHOOsKQNAJ0hVbSrnA1cr5+2SDfel1RfYt0W9FA6DoH/S5gAR\ndzT1u44QVwwp3U+eFpHphFy//uzxNMtCjjdkpzhYYhOCLNkDrlRPb+bcoL/6ePSr\n016PA7eEnuC305YU1Ml2WcCn7wQV8x90o33klJmEkWtXh3X39vYtI4nCPIvZn1eP\nVy+F+wWt4vN2b8oOdlzc2paOembbCo2B+Wapv5Y9peBvlbsDSgqtJABfK8KQq/jK\nYl3h5elIa1I3uNfczeHOnf1enLOUOlq630yeM/yHizz99G1g+z/guMh5+x/OHraW\niLkCDQRbMtt1ARAA1lNsWklhS9LoBdolTVtg65FfdFJr47pzKRGYIoGLbcJ155ND\nG+P8UrM06E/ah06EEWuvu2YyyYAz1iYGsCwHAXtbEJh+1tF0iOVx2vnZPgtIGE9V\nP95V5ZvWvB3bdke1z8HadDA+/Ve7fbwXXLa/z9QhSQgsJ8NS8KYnDDjI4EvQtv0i\nPVLY8+u8z6VyiV9RJyn8UEZEJdbFDF9AZAT8103w8SEo/cvIoUbVKZLGcXdAIjCa\ny04u6jsrMp9UGHZX7+srT+9YHDzQixei4IdmxUcqtiNR2/bFHpHCu1pzYjXj968D\n8Ng2txBXDgs16BF/9l++GWKz2dOSH0jdS6sFJ/Dmg7oYnJ2xKSJEmcnV8Z0M1n4w\nXR1t/KeKZe3aR+RXCAEVC5dQ3GbRW2+WboJ6ldgFcVcOv6iOSWP9TrLzFPOpCsIr\nnHE+cMBmPHq3dUm7KeYXQ6wWWmtXlw6widf7cBcGFeELpuU9klzqdKze8qo2oMkf\nrfxIq8zdciPxZXb/75dGWs6dLHQmDpo4MdQVskw5vvwHicMpUpGpxkX7X1XAfdQf\nyIHLGT4ZXuMLIMUPdzJE0Vwt/RtJrZ+feLSv/+0CkkpGHORYroGwIBrJ2RikgcV2\nbc98V/27Kz2ngUCEwnmlhIcrY4IGAAZzUAl0GLHSevPbAREu4fDW4Y+ztOsAEQEA\nAYkCHwQYAQIACQUCWzLbdQIbDAAKCRD3b2bD1AgnkusfD/9U4sPtZfMw6cII167A\nXRZOO195G7oiAnBUw5AW6EK0SAHVZcuW0LMMXnGe9f4UsEUgCNwo5mvLWPxzKqFq\n6/G3kEZVFwZ0qrlLoJPeHNbOcfkeZ9NgD/OhzQmdylM0IwGM9DMrm2YS4EVsmm2b\n53qKIfIyysp1yAGcTnBwBbZ85osNBl2KRDIPhMs0bnmGB7IAvwlSb+xm6vWKECkO\nlwQDO5Kg8YZ8+Z3pn/oS688t/fPXvWLZYUqwR63oWfIaPJI7Ahv2jJmgw1ofL81r\n2CE3T/OydtUeGLzqWJAB8sbUgT3ug0cjtxsHuroQBSYBND3XDb/EQh5GeVVnGKKH\ngESLFAoweoNjDSXrlIu1gFjCDHF4CqBRmNYKrNQjLmhCrSfwkytXESJwlLzFKY8P\nK1yZyTpDC9YK0G7qgrk7EHmH9JAZTQ5V65pp0vR9KvqTU5ewkQDIljD2f3FIqo2B\nSKNCQE+N6NjWaTeNlU75m+yZocKObSPg0zS8FAuSJetNtzXA7ouqk34OoIMQj4gq\nUnh/i1FcZAd4U6Dtr9aRZ6PeLlm6MJ/h582L6fJLNEu136UWDtJj5eBYEzX13l+d\nSC4PEHx7ZZRwQKptl9NkinLZGJztg175paUu8C34sAv+SQnM20c0pdOXAq9GKKhi\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----" @@ -18,7 +18,7 @@ }, { "name": "rt", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-rt-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-rt-e8.4-20250825", "image_type_tags": [ "edge-commit" ], @@ -29,7 +29,7 @@ }, { "name": "ha", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-ha-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-ha-e8.4-20250825", "image_type_tags": [ "ec2-ha", "ec2-sap" @@ -41,7 +41,7 @@ }, { "name": "sap", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-sap-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-sap-e8.4-20250825", "image_type_tags": [ "ec2-sap" ], @@ -52,7 +52,7 @@ }, { "name": "saphana", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-saphana-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-x86_64-sap-solutions-e8.4-20250825", "image_type_tags": [ "ec2-sap", "azure-sap-rhui" @@ -131,7 +131,7 @@ "aarch64": [ { "name": "baseos", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-aarch64-baseos-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-aarch64-baseos-e8.4-20250825", "gpgkeys": [ "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwEIACACGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSuBJPAAK\nCRAZni+R/UMdUfIkD/9m3HWv07uJG26R3KBexTo2FFu3rmZs+m2nfW8R3dBX+k0o\nAOFpgJCsNgKwU81LOPrkMN19G0+Yn/ZTCDD7cIQ7dhYuDyEX97xh4une/EhnnRuh\nASzR+1xYbj/HcYZIL9kbslgpebMn+AhxbUTQF/mziug3hLidR9Bzvygq0Q09E11c\nOZL4BU6J2HqxL+9m2F+tnLdfhL7MsAq9nbmWAOpkbGefc5SXBSq0sWfwoes3X3yD\nQ8B5Xqr9AxABU7oUB+wRqvY69ZCxi/BhuuJCUxY89ZmwXfkVxeHl1tYfROUwOnJO\nGYSbI/o41KBK4DkIiDcT7QqvqvCyudnxZdBjL2QU6OrIJvWmKs319qSF9m3mXRSt\nZzWtB89Pj5LZ6cdtuHvW9GO4qSoBLmAfB313pGkbgi1DE6tqCLHlA0yQ8zv99OWV\ncMDGmS7tVTZqfX1xQJ0N3bNORQNtikJC3G+zBCJzIeZleeDlMDQcww00yWU1oE7/\nTo2UmykMGc7o9iggFWR2g0PIcKsA/SXdRKWPqCHG2uKHBvdRTQGupdXQ1sbV+AHw\nycyA/9H/mp/NUSNM2cqnBDcZ6GhlHt59zWtEveiuU5fpTbp4GVcFXbW8jStj8j8z\n1HI3cywZO8+YNPzqyx0JWsidXGkfzkPHyS4jTG84lfu2JG8m/nqLnRSeKpl20Q==\n=79bX\n-----END PGP PUBLIC KEY BLOCK-----", "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----" @@ -139,7 +139,7 @@ }, { "name": "appstream", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-aarch64-appstream-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-aarch64-appstream-e8.4-20250825", "gpgkeys": [ "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwEIACACGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSuBJPAAK\nCRAZni+R/UMdUfIkD/9m3HWv07uJG26R3KBexTo2FFu3rmZs+m2nfW8R3dBX+k0o\nAOFpgJCsNgKwU81LOPrkMN19G0+Yn/ZTCDD7cIQ7dhYuDyEX97xh4une/EhnnRuh\nASzR+1xYbj/HcYZIL9kbslgpebMn+AhxbUTQF/mziug3hLidR9Bzvygq0Q09E11c\nOZL4BU6J2HqxL+9m2F+tnLdfhL7MsAq9nbmWAOpkbGefc5SXBSq0sWfwoes3X3yD\nQ8B5Xqr9AxABU7oUB+wRqvY69ZCxi/BhuuJCUxY89ZmwXfkVxeHl1tYfROUwOnJO\nGYSbI/o41KBK4DkIiDcT7QqvqvCyudnxZdBjL2QU6OrIJvWmKs319qSF9m3mXRSt\nZzWtB89Pj5LZ6cdtuHvW9GO4qSoBLmAfB313pGkbgi1DE6tqCLHlA0yQ8zv99OWV\ncMDGmS7tVTZqfX1xQJ0N3bNORQNtikJC3G+zBCJzIeZleeDlMDQcww00yWU1oE7/\nTo2UmykMGc7o9iggFWR2g0PIcKsA/SXdRKWPqCHG2uKHBvdRTQGupdXQ1sbV+AHw\nycyA/9H/mp/NUSNM2cqnBDcZ6GhlHt59zWtEveiuU5fpTbp4GVcFXbW8jStj8j8z\n1HI3cywZO8+YNPzqyx0JWsidXGkfzkPHyS4jTG84lfu2JG8m/nqLnRSeKpl20Q==\n=79bX\n-----END PGP PUBLIC KEY BLOCK-----", "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----" @@ -160,7 +160,7 @@ "ppc64le": [ { "name": "baseos", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-ppc64le-baseos-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-ppc64le-baseos-e8.4-20250825", "gpgkeys": [ "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwEIACACGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSuBJPAAK\nCRAZni+R/UMdUfIkD/9m3HWv07uJG26R3KBexTo2FFu3rmZs+m2nfW8R3dBX+k0o\nAOFpgJCsNgKwU81LOPrkMN19G0+Yn/ZTCDD7cIQ7dhYuDyEX97xh4une/EhnnRuh\nASzR+1xYbj/HcYZIL9kbslgpebMn+AhxbUTQF/mziug3hLidR9Bzvygq0Q09E11c\nOZL4BU6J2HqxL+9m2F+tnLdfhL7MsAq9nbmWAOpkbGefc5SXBSq0sWfwoes3X3yD\nQ8B5Xqr9AxABU7oUB+wRqvY69ZCxi/BhuuJCUxY89ZmwXfkVxeHl1tYfROUwOnJO\nGYSbI/o41KBK4DkIiDcT7QqvqvCyudnxZdBjL2QU6OrIJvWmKs319qSF9m3mXRSt\nZzWtB89Pj5LZ6cdtuHvW9GO4qSoBLmAfB313pGkbgi1DE6tqCLHlA0yQ8zv99OWV\ncMDGmS7tVTZqfX1xQJ0N3bNORQNtikJC3G+zBCJzIeZleeDlMDQcww00yWU1oE7/\nTo2UmykMGc7o9iggFWR2g0PIcKsA/SXdRKWPqCHG2uKHBvdRTQGupdXQ1sbV+AHw\nycyA/9H/mp/NUSNM2cqnBDcZ6GhlHt59zWtEveiuU5fpTbp4GVcFXbW8jStj8j8z\n1HI3cywZO8+YNPzqyx0JWsidXGkfzkPHyS4jTG84lfu2JG8m/nqLnRSeKpl20Q==\n=79bX\n-----END PGP PUBLIC KEY BLOCK-----", "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----" @@ -168,7 +168,7 @@ }, { "name": "appstream", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-ppc64le-appstream-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-ppc64le-appstream-e8.4-20250825", "gpgkeys": [ "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwEIACACGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSuBJPAAK\nCRAZni+R/UMdUfIkD/9m3HWv07uJG26R3KBexTo2FFu3rmZs+m2nfW8R3dBX+k0o\nAOFpgJCsNgKwU81LOPrkMN19G0+Yn/ZTCDD7cIQ7dhYuDyEX97xh4une/EhnnRuh\nASzR+1xYbj/HcYZIL9kbslgpebMn+AhxbUTQF/mziug3hLidR9Bzvygq0Q09E11c\nOZL4BU6J2HqxL+9m2F+tnLdfhL7MsAq9nbmWAOpkbGefc5SXBSq0sWfwoes3X3yD\nQ8B5Xqr9AxABU7oUB+wRqvY69ZCxi/BhuuJCUxY89ZmwXfkVxeHl1tYfROUwOnJO\nGYSbI/o41KBK4DkIiDcT7QqvqvCyudnxZdBjL2QU6OrIJvWmKs319qSF9m3mXRSt\nZzWtB89Pj5LZ6cdtuHvW9GO4qSoBLmAfB313pGkbgi1DE6tqCLHlA0yQ8zv99OWV\ncMDGmS7tVTZqfX1xQJ0N3bNORQNtikJC3G+zBCJzIeZleeDlMDQcww00yWU1oE7/\nTo2UmykMGc7o9iggFWR2g0PIcKsA/SXdRKWPqCHG2uKHBvdRTQGupdXQ1sbV+AHw\nycyA/9H/mp/NUSNM2cqnBDcZ6GhlHt59zWtEveiuU5fpTbp4GVcFXbW8jStj8j8z\n1HI3cywZO8+YNPzqyx0JWsidXGkfzkPHyS4jTG84lfu2JG8m/nqLnRSeKpl20Q==\n=79bX\n-----END PGP PUBLIC KEY BLOCK-----", "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----" @@ -178,7 +178,7 @@ "s390x": [ { "name": "baseos", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-s390x-baseos-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-s390x-baseos-e8.4-20250825", "gpgkeys": [ "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwEIACACGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSuBJPAAK\nCRAZni+R/UMdUfIkD/9m3HWv07uJG26R3KBexTo2FFu3rmZs+m2nfW8R3dBX+k0o\nAOFpgJCsNgKwU81LOPrkMN19G0+Yn/ZTCDD7cIQ7dhYuDyEX97xh4une/EhnnRuh\nASzR+1xYbj/HcYZIL9kbslgpebMn+AhxbUTQF/mziug3hLidR9Bzvygq0Q09E11c\nOZL4BU6J2HqxL+9m2F+tnLdfhL7MsAq9nbmWAOpkbGefc5SXBSq0sWfwoes3X3yD\nQ8B5Xqr9AxABU7oUB+wRqvY69ZCxi/BhuuJCUxY89ZmwXfkVxeHl1tYfROUwOnJO\nGYSbI/o41KBK4DkIiDcT7QqvqvCyudnxZdBjL2QU6OrIJvWmKs319qSF9m3mXRSt\nZzWtB89Pj5LZ6cdtuHvW9GO4qSoBLmAfB313pGkbgi1DE6tqCLHlA0yQ8zv99OWV\ncMDGmS7tVTZqfX1xQJ0N3bNORQNtikJC3G+zBCJzIeZleeDlMDQcww00yWU1oE7/\nTo2UmykMGc7o9iggFWR2g0PIcKsA/SXdRKWPqCHG2uKHBvdRTQGupdXQ1sbV+AHw\nycyA/9H/mp/NUSNM2cqnBDcZ6GhlHt59zWtEveiuU5fpTbp4GVcFXbW8jStj8j8z\n1HI3cywZO8+YNPzqyx0JWsidXGkfzkPHyS4jTG84lfu2JG8m/nqLnRSeKpl20Q==\n=79bX\n-----END PGP PUBLIC KEY BLOCK-----", "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----" @@ -186,11 +186,11 @@ }, { "name": "appstream", - "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-s390x-appstream-r8.4-20230809", + "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/rhvpn/el8/el8-s390x-appstream-e8.4-20250825", "gpgkeys": [ "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF\n0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF\n0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c\nu7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh\nXGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H\n5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW\n9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj\n/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1\nPcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY\nHVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF\nbuhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB\ntDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0\nLmNvbT6JAjYEEwEIACACGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSuBJPAAK\nCRAZni+R/UMdUfIkD/9m3HWv07uJG26R3KBexTo2FFu3rmZs+m2nfW8R3dBX+k0o\nAOFpgJCsNgKwU81LOPrkMN19G0+Yn/ZTCDD7cIQ7dhYuDyEX97xh4une/EhnnRuh\nASzR+1xYbj/HcYZIL9kbslgpebMn+AhxbUTQF/mziug3hLidR9Bzvygq0Q09E11c\nOZL4BU6J2HqxL+9m2F+tnLdfhL7MsAq9nbmWAOpkbGefc5SXBSq0sWfwoes3X3yD\nQ8B5Xqr9AxABU7oUB+wRqvY69ZCxi/BhuuJCUxY89ZmwXfkVxeHl1tYfROUwOnJO\nGYSbI/o41KBK4DkIiDcT7QqvqvCyudnxZdBjL2QU6OrIJvWmKs319qSF9m3mXRSt\nZzWtB89Pj5LZ6cdtuHvW9GO4qSoBLmAfB313pGkbgi1DE6tqCLHlA0yQ8zv99OWV\ncMDGmS7tVTZqfX1xQJ0N3bNORQNtikJC3G+zBCJzIeZleeDlMDQcww00yWU1oE7/\nTo2UmykMGc7o9iggFWR2g0PIcKsA/SXdRKWPqCHG2uKHBvdRTQGupdXQ1sbV+AHw\nycyA/9H/mp/NUSNM2cqnBDcZ6GhlHt59zWtEveiuU5fpTbp4GVcFXbW8jStj8j8z\n1HI3cywZO8+YNPzqyx0JWsidXGkfzkPHyS4jTG84lfu2JG8m/nqLnRSeKpl20Q==\n=79bX\n-----END PGP PUBLIC KEY BLOCK-----", "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nvt61kpkXoRGxjTlc6h+69aidSg==\n=ls8J\n-----END PGP PUBLIC KEY BLOCK-----" ] } ] -} \ No newline at end of file +} diff --git a/test/scripts/boot-image b/test/scripts/boot-image index 9347b6a08a..536b8e9065 100755 --- a/test/scripts/boot-image +++ b/test/scripts/boot-image @@ -20,11 +20,22 @@ def get_aws_config(): } +def get_azure_config(): + return { + "subscription": os.environ.get("AZURE_SUBSCRIPTION"), + "tenant": os.environ.get("AZURE_TENANT"), + "client_id": os.environ.get("AZURE_CLIENT_ID"), + "client_secret": os.environ.get("AZURE_CLIENT_SECRET"), + "resource_group": os.environ.get("AZURE_RESOURCE_GROUP") + } + + @contextlib.contextmanager -def create_ssh_key(): +def create_ssh_key(key_type = None): with TemporaryDirectory() as tmpdir: keypath = os.path.join(tmpdir, "testkey") - if ci_priv_key := os.environ.get("CI_PRIV_SSH_KEY_2"): + ci_priv_key = os.environ.get("CI_PRIV_SSH_KEY_2") + if not key_type and ci_priv_key: # running in CI: use key from env with open(keypath, "w", encoding="utf-8") as keyfile: keyfile.write(ci_priv_key + "\n") @@ -36,6 +47,9 @@ def create_ssh_key(): pubkey = out.decode() with open(keypath + ".pub", "w", encoding="utf-8") as pubkeyfile: pubkeyfile.write(pubkey) + elif key_type == "rsa": + cmd = ["ssh-keygen", "-t", "rsa", "-b", "2048", "-m", "pem", "-N", "", "-f", keypath] + testlib.runcmd_nc(cmd) else: # create an ssh key pair with empty password cmd = ["ssh-keygen", "-t", "ecdsa", "-b", "256", "-m", "pem", "-N", "", "-f", keypath] @@ -148,6 +162,31 @@ def boot_container(distro, arch, image_type, image_path, manifest_id): cmd_boot_aws(arch, image_name, privkey_file, pubkey_file, raw_image_path, [BASE_TEST_SCRIPT]) +def boot_vhd(distro, arch, image_path, config): + cmd = [BASE_TEST_SCRIPT, config] + with ensure_uncompressed(image_path) as raw_image_path: + with create_ssh_key(key_type="rsa") as (privkey, pubkey): + # a lot of resources have <=64 character naming constraint + name = f"{distro}-" + str(uuid.uuid4()) + + # pylint: disable=too-many-arguments,too-many-positional-arguments + az_config = get_azure_config() + cmd = ["go", "run", "./cmd/boot-azure", "run", + "--subscription", az_config["subscription"], + "--tenant", az_config["tenant"], + "--client-id", az_config["client_id"], + "--client-secret", az_config["client_secret"], + "--resource-group", az_config["resource_group"], + "--username", "osbuild", + "--ssh-privkey", privkey, + "--ssh-pubkey", pubkey, + "--vm-name", name, + "--arch", arch, + "--image-name", name, + raw_image_path, *cmd] + testlib.runcmd_nc(cmd) + + def main(): desc = "Boot an image in the cloud environment it is built for and validate the configuration" parser = argparse.ArgumentParser(description=desc) @@ -172,6 +211,8 @@ def main(): match image_type: case "ami" | "ec2" | "ec2-ha" | "ec2-sap" | "edge-ami": boot_ami(distro, arch, image_type, image_path, build_config_path) + case "vhd": + boot_vhd(distro, arch, image_path, build_config_path) case "iot-bootable-container": manifest_id = build_info["manifest-checksum"] boot_container(distro, arch, image_type, image_path, manifest_id) diff --git a/test/scripts/imgtestlib.py b/test/scripts/imgtestlib.py index 964682d7a6..a7d60e2c44 100644 --- a/test/scripts/imgtestlib.py +++ b/test/scripts/imgtestlib.py @@ -20,14 +20,19 @@ OS_RELEASE_FILE = "/etc/os-release" # image types that can be boot tested -CAN_BOOT_TEST = [ - "ami", - "ec2", - "ec2-ha", - "ec2-sap", - "edge-ami", - "iot-bootable-container", -] +CAN_BOOT_TEST = { + "*": [ + "ami", + "ec2", + "ec2-ha", + "ec2-sap", + "edge-ami", + "iot-bootable-container", + ], + "x86_64": [ + "vhd" + ], +} BIB_TYPES = [ "iot-bootable-container" @@ -307,7 +312,7 @@ def check_for_build(manifest_fname, build_info_dir, errors): print(" No PR/branch info available") image_type = dl_config["image-type"] - if image_type not in CAN_BOOT_TEST: + if image_type not in CAN_BOOT_TEST.get("*", []) + CAN_BOOT_TEST.get(dl_config["arch"], []): print(f" Boot testing for {image_type} is not yet supported") return False