tests/image: boot the vhd images on Azure
Previously, vhd images were tested using QEMU. This commit changes that to boot them in the actual Azure infrastructure. Azure VMs have quite a lot of dependencies - a network interface, a virtual network, a network security group, a public ip address and a disk. Azure CLI and Azure Portal handle the creation of all these resources internally. However, when using the API, the caller is responsible to create all these resources before creating an actual VM. To handle the creation of all the resources in the right order, a deployment is used. A deployment is a set of resources defined in a JSON document. It can optionally take parameters to customize each deployment. After the deployment is finished, the VM is up and ready to be tested using SSH. Sadly, the deployments are a bit hard to clean-up. One would expect that deleting a deployment removes all the deployed resources. However, it doesn't work this way and therefore it's needed to clean up all resources "manually". For this reason, our deployment sets a unique tag on all the resources created by the deployment. After this test is finished, the API is queried for all the resources with the tag and then, they're deleted in the right order.
This commit is contained in:
parent
10c016edca
commit
e235fdedb3
11 changed files with 669 additions and 25 deletions
328
cmd/osbuild-image-tests/azuretest/azure.go
Normal file
328
cmd/osbuild-image-tests/azuretest/azure.go
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
// +build integration
|
||||
|
||||
package azuretest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-09-01/network"
|
||||
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-05-01/resources"
|
||||
"github.com/Azure/azure-storage-blob-go/azblob"
|
||||
"github.com/Azure/go-autorest/autorest/azure/auth"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/upload/azure"
|
||||
)
|
||||
|
||||
// wrapErrorf returns error constructed using fmt.Errorf from format and any
|
||||
// other args. If innerError != nil, it's appended at the end of the new
|
||||
// error.
|
||||
func wrapErrorf(innerError error, format string, a ...interface{}) error {
|
||||
if innerError != nil {
|
||||
a = append(a, innerError)
|
||||
return fmt.Errorf(format+"\n\ninner error: %#s", a...)
|
||||
}
|
||||
|
||||
return fmt.Errorf(format, a...)
|
||||
}
|
||||
|
||||
type azureCredentials struct {
|
||||
azure.Credentials
|
||||
ContainerName string
|
||||
SubscriptionID string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
TenantID string
|
||||
Location string
|
||||
ResourceGroup string
|
||||
}
|
||||
|
||||
// getAzureCredentialsFromEnv gets the credentials from environment variables
|
||||
// If none of the environment variables is set, it returns nil.
|
||||
// If some but not all environment variables are set, it returns an error.
|
||||
func GetAzureCredentialsFromEnv() (*azureCredentials, error) {
|
||||
storageAccount, saExists := os.LookupEnv("AZURE_STORAGE_ACCOUNT")
|
||||
storageAccessKey, sakExists := os.LookupEnv("AZURE_STORAGE_ACCESS_KEY")
|
||||
containerName, cExists := os.LookupEnv("AZURE_CONTAINER_NAME")
|
||||
subscriptionId, siExists := os.LookupEnv("AZURE_SUBSCRIPTION_ID")
|
||||
clientId, ciExists := os.LookupEnv("AZURE_CLIENT_ID")
|
||||
clientSecret, csExists := os.LookupEnv("AZURE_CLIENT_SECRET")
|
||||
tenantId, tiExists := os.LookupEnv("AZURE_TENANT_ID")
|
||||
location, lExists := os.LookupEnv("AZURE_LOCATION")
|
||||
resourceGroup, rgExists := os.LookupEnv("AZURE_RESOURCE_GROUP")
|
||||
|
||||
// Workaround Travis security feature. If non of the variables is set, just ignore the test
|
||||
if !saExists && !sakExists && !cExists && !siExists && !ciExists && !csExists && !tiExists && !lExists && !rgExists {
|
||||
return nil, nil
|
||||
}
|
||||
// If only one/two of them are not set, then fail
|
||||
if !saExists || !sakExists || !cExists || !siExists || !ciExists || !csExists || !tiExists || !lExists || !rgExists {
|
||||
return nil, errors.New("not all required env variables were set")
|
||||
}
|
||||
|
||||
return &azureCredentials{
|
||||
Credentials: azure.Credentials{
|
||||
StorageAccount: storageAccount,
|
||||
StorageAccessKey: storageAccessKey,
|
||||
},
|
||||
ContainerName: containerName,
|
||||
SubscriptionID: subscriptionId,
|
||||
ClientID: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
TenantID: tenantId,
|
||||
Location: location,
|
||||
ResourceGroup: resourceGroup,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UploadImageToAzure mimics the upload feature of osbuild-composer.
|
||||
func UploadImageToAzure(c *azureCredentials, imagePath string, imageName string) error {
|
||||
metadata := azure.ImageMetadata{
|
||||
ContainerName: c.ContainerName,
|
||||
ImageName: imageName,
|
||||
}
|
||||
err := azure.UploadImage(c.Credentials, metadata, imagePath, 16)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upload to azure failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteImageFromAzure deletes the image uploaded by osbuild-composer
|
||||
// (or UpluadImageToAzure method).
|
||||
func DeleteImageFromAzure(c *azureCredentials, imageName string) error {
|
||||
// Create a default request pipeline using your storage account name and account key.
|
||||
credential, err := azblob.NewSharedKeyCredential(c.StorageAccount, c.StorageAccessKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := azblob.NewPipeline(credential, azblob.PipelineOptions{})
|
||||
|
||||
// get storage account blob service URL endpoint.
|
||||
URL, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s", c.StorageAccount, c.ContainerName))
|
||||
|
||||
// Create a ContainerURL object that wraps the container URL and a request
|
||||
// pipeline to make requests.
|
||||
containerURL := azblob.NewContainerURL(*URL, p)
|
||||
|
||||
// Create the container, use a never-expiring context
|
||||
ctx := context.Background()
|
||||
|
||||
blobURL := containerURL.NewPageBlobURL(imageName)
|
||||
|
||||
_, err = blobURL.Delete(ctx, azblob.DeleteSnapshotsOptionInclude, azblob.BlobAccessConditions{})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete the image: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type resourceType struct {
|
||||
resType string
|
||||
apiVersion string
|
||||
}
|
||||
|
||||
// resourcesTypesToDelete serves two purposes:
|
||||
// 1) The WithBootedImageInAzure method tags all the created resources and
|
||||
// we can get the list of resources with that tag. However, it's needed to
|
||||
// delete them in right order because of inner dependencies.
|
||||
// 2) The resources.Client.DeleteByID method requires the API version to be
|
||||
// passed in. Therefore we need to way to get API version for a given
|
||||
// resource type.
|
||||
var resourcesTypesToDelete = []resourceType{
|
||||
{
|
||||
resType: "Microsoft.Compute/virtualMachines",
|
||||
apiVersion: "2019-07-01",
|
||||
},
|
||||
{
|
||||
resType: "Microsoft.Network/networkInterfaces",
|
||||
apiVersion: "2019-09-01",
|
||||
},
|
||||
{
|
||||
resType: "Microsoft.Network/publicIPAddresses",
|
||||
apiVersion: "2019-09-01",
|
||||
},
|
||||
{
|
||||
resType: "Microsoft.Network/networkSecurityGroups",
|
||||
apiVersion: "2019-09-01",
|
||||
},
|
||||
{
|
||||
resType: "Microsoft.Network/virtualNetworks",
|
||||
apiVersion: "2019-09-01",
|
||||
},
|
||||
{
|
||||
resType: "Microsoft.Compute/disks",
|
||||
apiVersion: "2019-07-01",
|
||||
},
|
||||
{
|
||||
resType: "Microsoft.Compute/images",
|
||||
apiVersion: "2019-07-01",
|
||||
},
|
||||
}
|
||||
|
||||
// readPublicKey reads the public key from a file and returns it as a string
|
||||
func readPublicKey(publicKeyFile string) (string, error) {
|
||||
publicKey, err := ioutil.ReadFile(publicKeyFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot read the public key file: %v", err)
|
||||
}
|
||||
|
||||
return string(publicKey), nil
|
||||
}
|
||||
|
||||
// deleteResource is a convenient wrapper around Azure SDK to delete a resource
|
||||
func deleteResource(client resources.Client, id string, apiVersion string) error {
|
||||
deleteFuture, err := client.DeleteByID(context.Background(), id, apiVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete the resourceType %s: %v", id, err)
|
||||
}
|
||||
|
||||
err = deleteFuture.WaitForCompletionRef(context.Background(), client.BaseClient.Client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("waiting for the resourceType %s deletion failed: %v", id, err)
|
||||
}
|
||||
|
||||
_, err = deleteFuture.Result(client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot retrieve the result of %s deletion: %v", id, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// withBootedImageInAzure runs the function f in the context of booted
|
||||
// image in Azure
|
||||
func WithBootedImageInAzure(creds *azureCredentials, imageName, testId, publicKeyFile string, f func(address string) error) (retErr error) {
|
||||
publicKey, err := readPublicKey(publicKeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientCredentialsConfig := auth.NewClientCredentialsConfig(creds.ClientID, creds.ClientSecret, creds.TenantID)
|
||||
authorizer, err := clientCredentialsConfig.Authorizer()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create the authorizer: %v", err)
|
||||
}
|
||||
|
||||
template, err := loadDeploymentTemplate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Azure requires a lot of names - for a virtual machine, a virtual network,
|
||||
// a virtual interface and so on and so forth.
|
||||
// In the Go code, it's just need to know the public IP address name,
|
||||
// the tag name and the deployment name. Let's set these here to names
|
||||
// based on the test id.
|
||||
// The rest of the names are set from the test id inside the deployment
|
||||
// template because they're irrelevant to the Go code.
|
||||
deploymentName := testId
|
||||
tag := "tag-" + testId
|
||||
publicIPAddressName := "address-" + testId
|
||||
imagePath := fmt.Sprintf("https://%s.blob.core.windows.net/%s/%s", creds.StorageAccount, creds.ContainerName, imageName)
|
||||
|
||||
parameters := deploymentParameters{
|
||||
Location: newDeploymentParameter(creds.Location),
|
||||
TestId: newDeploymentParameter(testId),
|
||||
Tag: newDeploymentParameter(tag),
|
||||
PublicIPAddressName: newDeploymentParameter(publicIPAddressName),
|
||||
ImagePath: newDeploymentParameter(imagePath),
|
||||
AdminUsername: newDeploymentParameter("redhat"),
|
||||
AdminPublicKey: newDeploymentParameter(publicKey),
|
||||
}
|
||||
|
||||
deploymentsClient := resources.NewDeploymentsClient(creds.SubscriptionID)
|
||||
deploymentsClient.Authorizer = authorizer
|
||||
|
||||
deploymentFuture, err := deploymentsClient.CreateOrUpdate(context.Background(), creds.ResourceGroup, deploymentName, resources.Deployment{
|
||||
Properties: &resources.DeploymentProperties{
|
||||
Mode: resources.Incremental,
|
||||
Template: template,
|
||||
Parameters: parameters,
|
||||
},
|
||||
})
|
||||
|
||||
// Let's registed the clean-up function as soon as possible.
|
||||
defer func() {
|
||||
resourcesClient := resources.NewClient(creds.SubscriptionID)
|
||||
resourcesClient.Authorizer = authorizer
|
||||
|
||||
// find all the resources we marked with a tag during the deployment
|
||||
filter := fmt.Sprintf("tagName eq 'osbuild-composer-image-test' and tagValue eq '%s'", tag)
|
||||
resourceList, err := resourcesClient.ListByResourceGroup(context.Background(), creds.ResourceGroup, filter, "", nil)
|
||||
if err != nil {
|
||||
retErr = wrapErrorf(retErr, "listing of resources failed: %v", err)
|
||||
} else {
|
||||
|
||||
// delete all the found resources
|
||||
for _, resourceType := range resourcesTypesToDelete {
|
||||
for _, resource := range resourceList.Values() {
|
||||
if *resource.Type != resourceType.resType {
|
||||
continue
|
||||
}
|
||||
|
||||
err := deleteResource(resourcesClient, *resource.ID, resourceType.apiVersion)
|
||||
if err != nil {
|
||||
retErr = wrapErrorf(retErr, "cannot delete the resource %s: %v", *resource.ID, err)
|
||||
// do not return here, try deleting as much as possible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the deployment
|
||||
// This actually does not delete any resources created by the
|
||||
// deployment as one might think. Therefore the code above
|
||||
// and the tagging are needed.
|
||||
result, err := deploymentsClient.Delete(context.Background(), creds.ResourceGroup, deploymentName)
|
||||
if err != nil {
|
||||
retErr = wrapErrorf(retErr, "cannot create the request for the deployment deletion: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = result.WaitForCompletionRef(context.Background(), deploymentsClient.Client)
|
||||
if err != nil {
|
||||
retErr = wrapErrorf(retErr, "waiting for the deployment deletion failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = result.Result(deploymentsClient)
|
||||
if err != nil {
|
||||
retErr = wrapErrorf(retErr, "cannot retrieve the deployment deletion result: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating a deployment failed: %v", err)
|
||||
}
|
||||
|
||||
err = deploymentFuture.WaitForCompletionRef(context.Background(), deploymentsClient.Client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("waiting for deployment completion failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = deploymentFuture.Result(deploymentsClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("retrieving the deployment result failed: %v", err)
|
||||
}
|
||||
|
||||
// get the IP address
|
||||
publicIPAddressClient := network.NewPublicIPAddressesClient(creds.SubscriptionID)
|
||||
publicIPAddressClient.Authorizer = authorizer
|
||||
|
||||
publicIPAddress, err := publicIPAddressClient.Get(context.Background(), creds.ResourceGroup, publicIPAddressName, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get the ip address details: %v", err)
|
||||
}
|
||||
|
||||
return f(*publicIPAddress.IPAddress)
|
||||
}
|
||||
52
cmd/osbuild-image-tests/azuretest/deployment.go
Normal file
52
cmd/osbuild-image-tests/azuretest/deployment.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// +build integration
|
||||
|
||||
package azuretest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/cmd/osbuild-image-tests/constants"
|
||||
)
|
||||
|
||||
// loadDeploymentTemplate loads the deployment template from the specified
|
||||
// path and return it as a "dynamically" typed object
|
||||
func loadDeploymentTemplate() (interface{}, error) {
|
||||
f, err := os.Open(constants.TestPaths.AzureDeploymentTemplate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot open the deployment file: %v", err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
var result interface{}
|
||||
|
||||
err = json.NewDecoder(f).Decode(&result)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot decode the deployment file: %v", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// struct for encoding a deployment parameter
|
||||
type deploymentParameter struct {
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func newDeploymentParameter(value interface{}) deploymentParameter {
|
||||
return deploymentParameter{Value: value}
|
||||
}
|
||||
|
||||
// struct for encoding deployment parameters
|
||||
type deploymentParameters struct {
|
||||
Location deploymentParameter `json:"location"`
|
||||
TestId deploymentParameter `json:"testId"`
|
||||
Tag deploymentParameter `json:"tag"`
|
||||
PublicIPAddressName deploymentParameter `json:"publicIPAddressName"`
|
||||
ImagePath deploymentParameter `json:"imagePath"`
|
||||
AdminUsername deploymentParameter `json:"adminUsername"`
|
||||
AdminPublicKey deploymentParameter `json:"adminPublicKey"`
|
||||
}
|
||||
|
|
@ -18,15 +18,17 @@ func GetOsbuildCommand(store string) *exec.Cmd {
|
|||
}
|
||||
|
||||
var TestPaths = struct {
|
||||
ImageInfo string
|
||||
PrivateKey string
|
||||
TestCasesDirectory string
|
||||
UserData string
|
||||
MetaData string
|
||||
ImageInfo string
|
||||
PrivateKey string
|
||||
TestCasesDirectory string
|
||||
UserData string
|
||||
MetaData string
|
||||
AzureDeploymentTemplate string
|
||||
}{
|
||||
ImageInfo: "tools/image-info",
|
||||
PrivateKey: "test/keyring/id_rsa",
|
||||
TestCasesDirectory: "test/cases",
|
||||
UserData: "test/cloud-init/user-data",
|
||||
MetaData: "test/cloud-init/meta-data",
|
||||
ImageInfo: "tools/image-info",
|
||||
PrivateKey: "test/keyring/id_rsa",
|
||||
TestCasesDirectory: "test/cases",
|
||||
UserData: "test/cloud-init/user-data",
|
||||
MetaData: "test/cloud-init/meta-data",
|
||||
AzureDeploymentTemplate: "test/azure-deployment-template.json",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,15 +14,17 @@ func GetOsbuildCommand(store string) *exec.Cmd {
|
|||
}
|
||||
|
||||
var TestPaths = struct {
|
||||
ImageInfo string
|
||||
PrivateKey string
|
||||
TestCasesDirectory string
|
||||
UserData string
|
||||
MetaData string
|
||||
ImageInfo string
|
||||
PrivateKey string
|
||||
TestCasesDirectory string
|
||||
UserData string
|
||||
MetaData string
|
||||
AzureDeploymentTemplate string
|
||||
}{
|
||||
ImageInfo: "/usr/libexec/osbuild-composer/image-info",
|
||||
PrivateKey: "/usr/share/tests/osbuild-composer/keyring/id_rsa",
|
||||
TestCasesDirectory: "/usr/share/tests/osbuild-composer/cases",
|
||||
UserData: "/usr/share/tests/osbuild-composer/cloud-init/user-data",
|
||||
MetaData: "/usr/share/tests/osbuild-composer/cloud-init/meta-data",
|
||||
ImageInfo: "/usr/libexec/osbuild-composer/image-info",
|
||||
PrivateKey: "/usr/share/tests/osbuild-composer/keyring/id_rsa",
|
||||
TestCasesDirectory: "/usr/share/tests/osbuild-composer/cases",
|
||||
UserData: "/usr/share/tests/osbuild-composer/cloud-init/user-data",
|
||||
MetaData: "/usr/share/tests/osbuild-composer/cloud-init/meta-data",
|
||||
AzureDeploymentTemplate: "/usr/share/tests/osbuild-composer/azure-deployment-template.json",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/cmd/osbuild-image-tests/azuretest"
|
||||
"github.com/osbuild/osbuild-composer/cmd/osbuild-image-tests/constants"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
)
|
||||
|
|
@ -262,6 +263,43 @@ func testBootUsingAWS(t *testing.T, imagePath string) {
|
|||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func testBootUsingAzure(t *testing.T, imagePath string) {
|
||||
creds, err := azuretest.GetAzureCredentialsFromEnv()
|
||||
require.NoError(t, err)
|
||||
|
||||
// if no credentials are given, fall back to qemu
|
||||
if creds == nil {
|
||||
log.Print("no Azure credentials given, falling back to booting using qemu")
|
||||
testBootUsingQemu(t, imagePath)
|
||||
return
|
||||
}
|
||||
|
||||
// create a random test id to name all the resources used in this test
|
||||
testId, err := generateRandomString("")
|
||||
require.NoError(t, err)
|
||||
|
||||
imageName := "image-" + testId + ".vhd"
|
||||
|
||||
// the following line should be done by osbuild-composer at some point
|
||||
err = azuretest.UploadImageToAzure(creds, imagePath, imageName)
|
||||
require.NoErrorf(t, err, "upload to azure failed, resources could have been leaked")
|
||||
|
||||
// delete the image after the test is over
|
||||
defer func() {
|
||||
err = azuretest.DeleteImageFromAzure(creds, imageName)
|
||||
require.NoErrorf(t, err, "cannot delete the azure image, resources could have been leaked")
|
||||
}()
|
||||
|
||||
// boot the uploaded image and try to connect to it
|
||||
err = withSSHKeyPair(func(privateKey, publicKey string) error {
|
||||
return azuretest.WithBootedImageInAzure(creds, imageName, testId, publicKey, func(address string) error {
|
||||
testSSH(t, address, privateKey, nil)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// testBoot tests if the image is able to successfully boot
|
||||
// Before the test it boots the image respecting the specified bootType.
|
||||
// The test passes if the function is able to connect to the image via ssh
|
||||
|
|
@ -281,6 +319,9 @@ func testBoot(t *testing.T, imagePath string, bootType string, outputID string)
|
|||
case "aws":
|
||||
testBootUsingAWS(t, imagePath)
|
||||
|
||||
case "azure":
|
||||
testBootUsingAzure(t, imagePath)
|
||||
|
||||
default:
|
||||
panic("unknown boot type!")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ BuildRequires: systemd
|
|||
BuildRequires: systemd-rpm-macros
|
||||
BuildRequires: git
|
||||
BuildRequires: golang(github.com/aws/aws-sdk-go)
|
||||
BuildRequires: golang(github.com/Azure/azure-sdk-for-go)
|
||||
BuildRequires: golang(github.com/Azure/azure-storage-blob-go/azblob)
|
||||
BuildRequires: golang(github.com/BurntSushi/toml)
|
||||
BuildRequires: golang(github.com/coreos/go-semver/semver)
|
||||
|
|
@ -113,6 +114,9 @@ install -m 0755 -vp _bin/osbuild-image-tests %{buildroot}%{_libex
|
|||
install -m 0755 -vp _bin/osbuild-rcm-tests %{buildroot}%{_libexecdir}/tests/osbuild-composer/
|
||||
install -m 0755 -vp tools/image-info %{buildroot}%{_libexecdir}/osbuild-composer/
|
||||
|
||||
install -m 0755 -vd %{buildroot}%{_datadir}/tests/osbuild-composer
|
||||
install -m 0644 -vp test/azure-deployment-template.json %{buildroot}%{_datadir}/tests/osbuild-composer/
|
||||
|
||||
install -m 0755 -vd %{buildroot}%{_datadir}/tests/osbuild-composer/cases
|
||||
install -m 0644 -vp test/cases/* %{buildroot}%{_datadir}/tests/osbuild-composer/cases/
|
||||
install -m 0755 -vd %{buildroot}%{_datadir}/tests/osbuild-composer/keyring
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ BuildRequires: systemd
|
|||
BuildRequires: systemd-rpm-macros
|
||||
BuildRequires: git
|
||||
BuildRequires: golang(github.com/aws/aws-sdk-go)
|
||||
BuildRequires: golang(github.com/Azure/azure-sdk-for-go)
|
||||
BuildRequires: golang(github.com/Azure/azure-storage-blob-go/azblob)
|
||||
BuildRequires: golang(github.com/BurntSushi/toml)
|
||||
BuildRequires: golang(github.com/coreos/go-semver/semver)
|
||||
|
|
@ -127,6 +128,9 @@ install -m 0755 -vp _bin/osbuild-image-tests %{buildroot}%{_libex
|
|||
install -m 0755 -vp _bin/osbuild-rcm-tests %{buildroot}%{_libexecdir}/tests/osbuild-composer/
|
||||
install -m 0755 -vp tools/image-info %{buildroot}%{_libexecdir}/osbuild-composer/
|
||||
|
||||
install -m 0755 -vd %{buildroot}%{_datadir}/tests/osbuild-composer
|
||||
install -m 0644 -vp test/azure-deployment-template.json %{buildroot}%{_datadir}/tests/osbuild-composer/
|
||||
|
||||
install -m 0755 -vd %{buildroot}%{_datadir}/tests/osbuild-composer/cases
|
||||
install -m 0644 -vp test/cases/* %{buildroot}%{_datadir}/tests/osbuild-composer/cases/
|
||||
install -m 0755 -vd %{buildroot}%{_datadir}/tests/osbuild-composer/keyring
|
||||
|
|
|
|||
211
test/azure-deployment-template.json
Normal file
211
test/azure-deployment-template.json
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
{
|
||||
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"testId": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicIPAddressName": {
|
||||
"type": "string"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string"
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"imagePath": {
|
||||
"type": "string"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string"
|
||||
},
|
||||
"adminPublicKey": {
|
||||
"type": "secureString"
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"subnetRef": "[concat(variables('vnetId'), '/subnets/default')]",
|
||||
"networkInterfaceName": "[concat('iface-', parameters('testId'))]",
|
||||
"networkSecurityGroupName": "[concat('nsg-', parameters('testId'))]",
|
||||
"virtualNetworkName": "[concat('vnet-', parameters('testId'))]",
|
||||
"publicIPAddressName": "[concat('ip-', parameters('testId'))]",
|
||||
"virtualMachineName": "[concat('vm-', parameters('testId'))]",
|
||||
"diskName": "[concat('disk-', parameters('testId'))]",
|
||||
"imageName": "[concat('image-', parameters('testId'))]",
|
||||
"nsgId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]",
|
||||
"vnetId": "[resourceId(resourceGroup().name,'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[variables('networkInterfaceName')]",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"apiVersion": "2019-07-01",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkSecurityGroups/', variables('networkSecurityGroupName'))]",
|
||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]",
|
||||
"[concat('Microsoft.Network/publicIpAddresses/', parameters('publicIPAddressName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"subnet": {
|
||||
"id": "[variables('subnetRef')]"
|
||||
},
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"publicIpAddress": {
|
||||
"id": "[resourceId(resourceGroup().name, 'Microsoft.Network/publicIpAddresses', parameters('publicIPAddressName'))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('nsgId')]"
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"osbuild-composer-image-test": "[parameters('tag')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[variables('networkSecurityGroupName')]",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"apiVersion": "2019-02-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"securityRules": [
|
||||
{
|
||||
"name": "SSH",
|
||||
"properties": {
|
||||
"priority": 300,
|
||||
"protocol": "TCP",
|
||||
"access": "Allow",
|
||||
"direction": "Inbound",
|
||||
"sourceAddressPrefix": "*",
|
||||
"sourcePortRange": "*",
|
||||
"destinationAddressPrefix": "*",
|
||||
"destinationPortRange": "22"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tags": {
|
||||
"osbuild-composer-image-test": "[parameters('tag')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[variables('virtualNetworkName')]",
|
||||
"type": "Microsoft.Network/virtualNetworks",
|
||||
"apiVersion": "2019-09-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"addressSpace": {
|
||||
"addressPrefixes": [
|
||||
"10.0.5.0/24"
|
||||
]
|
||||
},
|
||||
"subnets": [
|
||||
{
|
||||
"name": "default",
|
||||
"properties": {
|
||||
"addressPrefix": "10.0.5.0/24"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tags": {
|
||||
"osbuild-composer-image-test": "[parameters('tag')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[parameters('publicIPAddressName')]",
|
||||
"type": "Microsoft.Network/publicIpAddresses",
|
||||
"apiVersion": "2019-02-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"publicIpAllocationMethod": "Dynamic"
|
||||
},
|
||||
"sku": {
|
||||
"name": "Basic"
|
||||
},
|
||||
"tags": {
|
||||
"osbuild-composer-image-test": "[parameters('tag')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[variables('imageName')]",
|
||||
"type": "Microsoft.Compute/images",
|
||||
"apiVersion": "2019-07-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"hyperVGeneration": "V1",
|
||||
"storageProfile": {
|
||||
"osDisk": {
|
||||
"osType": "Linux",
|
||||
"blobUri": "[parameters('imagePath')]",
|
||||
"osState": "Generalized"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"osbuild-composer-image-test": "[parameters('tag')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[variables('virtualMachineName')]",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"apiVersion": "2019-07-01",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('networkInterfaceName'))]",
|
||||
"[concat('Microsoft.Compute/images/', variables('imageName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "Standard_B1s"
|
||||
},
|
||||
"storageProfile": {
|
||||
"imageReference": {
|
||||
"id": "[resourceId(resourceGroup().name, 'Microsoft.Compute/images', variables('imageName'))]"
|
||||
},
|
||||
"osDisk": {
|
||||
"caching": "ReadWrite",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "Standard_LRS"
|
||||
},
|
||||
"name": "[variables('diskName')]",
|
||||
"createOption": "FromImage"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[variables('virtualMachineName')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"linuxConfiguration": {
|
||||
"disablePasswordAuthentication": true,
|
||||
"ssh": {
|
||||
"publicKeys": [
|
||||
{
|
||||
"path": "[concat('/home/', parameters('adminUsername'), '/.ssh/authorized_keys')]",
|
||||
"keyData": "[parameters('adminPublicKey')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"osbuild-composer-image-test": "[parameters('tag')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"boot": {
|
||||
"type": "qemu"
|
||||
"type": "azure"
|
||||
},
|
||||
"compose-request": {
|
||||
"distro": "fedora-30",
|
||||
|
|
@ -7427,4 +7427,4 @@
|
|||
],
|
||||
"timezone": "UTC"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"boot": {
|
||||
"type": "qemu"
|
||||
"type": "azure"
|
||||
},
|
||||
"compose-request": {
|
||||
"distro": "fedora-31",
|
||||
|
|
@ -9051,4 +9051,4 @@
|
|||
"waagent.service"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@
|
|||
},
|
||||
"vhd": {
|
||||
"boot": {
|
||||
"type": "qemu"
|
||||
"type": "azure"
|
||||
},
|
||||
"compose-request": {
|
||||
"distro": "",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue