The openstack boot test often ruins our days with: Waiting for instance 63ac19be-2e19-44e2-8bef-9770d68a190c to become Active failed: A timeout occurred I decided to investigate. It turns out the first boot of an image can take up to 18 minutes. The subsequent ones are usually much faster (but don't rely on this fact, I saw 15 minutes there). This commit bumps the timeout to 30 minutes. This should be plenty of time for the instance to spin up and get into the ACTIVE state. Honestly, I'm not very happy with the solution but it should help with the failing Schutzbot. As a follow up, I will reach to the PSI OpenStack team and ask them if we could somehow speed up the process (maybe by using another flavor, ci.m1.medium.ephemeral just might be slow for some reason, I don't know). Anyway, this should help us in the short term because I strongly believe that a slow test is still better than a failing one. Signed-off-by: Ondřej Budai <ondrej@budai.cz>
133 lines
4 KiB
Go
133 lines
4 KiB
Go
// +build integration
|
|
|
|
package openstacktest
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/gophercloud/gophercloud"
|
|
"github.com/gophercloud/gophercloud/openstack"
|
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
|
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata"
|
|
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
|
)
|
|
|
|
const WaitTimeout = 30 * 60 // 30 minutes in seconds
|
|
|
|
func UploadImageToOpenStack(p *gophercloud.ProviderClient, imagePath string, imageName string) (*images.Image, error) {
|
|
client, err := openstack.NewImageServiceV2(p, gophercloud.EndpointOpts{
|
|
Region: os.Getenv("OS_REGION_NAME"),
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error creating ImageService client: %v", err)
|
|
}
|
|
|
|
// create a new image which gives us the ID
|
|
image, err := images.Create(client, images.CreateOpts{
|
|
Name: imageName,
|
|
DiskFormat: "qcow2",
|
|
ContainerFormat: "bare",
|
|
}).Extract()
|
|
if err != nil {
|
|
return image, fmt.Errorf("Creating image failed: %v", err)
|
|
}
|
|
|
|
// then upload the actual binary data
|
|
imageData, err := os.Open(imagePath)
|
|
if err != nil {
|
|
return image, fmt.Errorf("Error opening %s: %v", imagePath, err)
|
|
}
|
|
defer imageData.Close()
|
|
|
|
err = imagedata.Upload(client, image.ID, imageData).ExtractErr()
|
|
if err != nil {
|
|
return image, fmt.Errorf("Upload to OpenStack failed: %v", err)
|
|
}
|
|
|
|
// wait for the status to change from Queued to Active
|
|
err = gophercloud.WaitFor(WaitTimeout, func() (bool, error) {
|
|
actual, err := images.Get(client, image.ID).Extract()
|
|
return actual.Status == images.ImageStatusActive, err
|
|
})
|
|
if err != nil {
|
|
return image, fmt.Errorf("Waiting for image to become Active failed: %v", err)
|
|
}
|
|
|
|
return image, nil
|
|
}
|
|
|
|
func DeleteImageFromOpenStack(p *gophercloud.ProviderClient, imageUUID string) error {
|
|
client, err := openstack.NewImageServiceV2(p, gophercloud.EndpointOpts{
|
|
Region: os.Getenv("OS_REGION_NAME"),
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating ImageService client: %v", err)
|
|
}
|
|
|
|
err = images.Delete(client, imageUUID).ExtractErr()
|
|
if err != nil {
|
|
return fmt.Errorf("cannot delete the image: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func WithBootedImageInOpenStack(p *gophercloud.ProviderClient, imageID, userData string, f func(address string) error) (retErr error) {
|
|
client, err := openstack.NewComputeV2(p, gophercloud.EndpointOpts{
|
|
Region: os.Getenv("OS_REGION_NAME"),
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating Compute client: %v", err)
|
|
}
|
|
|
|
server, err := servers.Create(client, servers.CreateOpts{
|
|
Name: "osbuild-composer-vm-for-" + imageID,
|
|
FlavorRef: "77b8cf27-be16-40d9-95b1-81db4522be1e", // ci.m1.medium.ephemeral
|
|
Networks: []servers.Network{ // provider_net_cci_2
|
|
servers.Network{UUID: "74e8faa7-87ba-41b2-a000-438013194814"},
|
|
},
|
|
ImageRef: imageID,
|
|
UserData: []byte(userData),
|
|
}).Extract()
|
|
if err != nil {
|
|
return fmt.Errorf("Cannot create instance: %v", err)
|
|
}
|
|
|
|
// cleanup
|
|
defer func() {
|
|
err := servers.ForceDelete(client, server.ID).ExtractErr()
|
|
if err != nil {
|
|
fmt.Printf("Force deleting instance %s failed: %v", server.ID, err)
|
|
return
|
|
}
|
|
}()
|
|
|
|
// wait for the status to become Active
|
|
err = servers.WaitForStatus(client, server.ID, "ACTIVE", WaitTimeout)
|
|
if err != nil {
|
|
return fmt.Errorf("Waiting for instance %s to become Active failed: %v", server.ID, err)
|
|
}
|
|
|
|
// get server details again to refresh the IP addresses
|
|
server, err = servers.Get(client, server.ID).Extract()
|
|
if err != nil {
|
|
return fmt.Errorf("Cannot get instance details: %v\n", err)
|
|
}
|
|
|
|
// server.AccessIPv4 is empty so list all addresses and
|
|
// get the first fixed one. ssh should be equally happy with v4 or v6
|
|
var fixedIP string
|
|
for _, networkAddresses := range server.Addresses["provider_net_cci_2"].([]interface{}) {
|
|
address := networkAddresses.(map[string]interface{})
|
|
if address["OS-EXT-IPS:type"] == "fixed" {
|
|
fixedIP = address["addr"].(string)
|
|
break
|
|
}
|
|
}
|
|
if fixedIP == "" {
|
|
return fmt.Errorf("Cannot find IP address for instance %s", server.ID)
|
|
}
|
|
|
|
return f(fixedIP)
|
|
}
|