|
Packit |
63bb0d |
// +build integration
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
package vmwaretest
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
import (
|
|
Packit |
63bb0d |
"errors"
|
|
Packit |
63bb0d |
"fmt"
|
|
Packit |
63bb0d |
"io/ioutil"
|
|
Packit |
63bb0d |
"os"
|
|
Packit |
63bb0d |
"os/exec"
|
|
Packit |
63bb0d |
"path/filepath"
|
|
Packit |
63bb0d |
"strings"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// importing the packages registers these cli commands
|
|
Packit |
63bb0d |
"github.com/vmware/govmomi/govc/cli"
|
|
Packit |
63bb0d |
_ "github.com/vmware/govmomi/govc/datastore"
|
|
Packit |
63bb0d |
_ "github.com/vmware/govmomi/govc/importx"
|
|
Packit |
63bb0d |
_ "github.com/vmware/govmomi/govc/vm"
|
|
Packit |
63bb0d |
_ "github.com/vmware/govmomi/govc/vm/guest"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
const WaitTimeout = 6000 // in seconds
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
type AuthOptions struct {
|
|
Packit |
63bb0d |
Host string
|
|
Packit |
63bb0d |
Username string
|
|
Packit |
63bb0d |
Password string
|
|
Packit |
63bb0d |
Datacenter string
|
|
Packit |
63bb0d |
Cluster string
|
|
Packit |
63bb0d |
Network string
|
|
Packit |
63bb0d |
Datastore string
|
|
Packit |
63bb0d |
Folder string
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func AuthOptionsFromEnv() (*AuthOptions, error) {
|
|
Packit |
63bb0d |
host, hostExists := os.LookupEnv("GOVMOMI_URL")
|
|
Packit |
63bb0d |
username, userExists := os.LookupEnv("GOVMOMI_USERNAME")
|
|
Packit |
63bb0d |
password, pwdExists := os.LookupEnv("GOVMOMI_PASSWORD")
|
|
Packit |
63bb0d |
datacenter, dcExists := os.LookupEnv("GOVMOMI_DATACENTER")
|
|
Packit |
63bb0d |
cluster, clusterExists := os.LookupEnv("GOVMOMI_CLUSTER")
|
|
Packit |
63bb0d |
network, netExists := os.LookupEnv("GOVMOMI_NETWORK")
|
|
Packit |
63bb0d |
datastore, dsExists := os.LookupEnv("GOVMOMI_DATASTORE")
|
|
Packit |
63bb0d |
folder, folderExists := os.LookupEnv("GOVMOMI_FOLDER")
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// If only one/two of them are not set, then fail
|
|
Packit |
63bb0d |
if !hostExists {
|
|
Packit |
63bb0d |
return nil, errors.New("GOVMOMI_URL not set")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if !userExists {
|
|
Packit |
63bb0d |
return nil, errors.New("GOVMOMI_USERNAME not set")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if !pwdExists {
|
|
Packit |
63bb0d |
return nil, errors.New("GOVMOMI_PASSWORD not set")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if !dcExists {
|
|
Packit |
63bb0d |
return nil, errors.New("GOVMOMI_DATACENTER not set")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if !clusterExists {
|
|
Packit |
63bb0d |
return nil, errors.New("GOVMOMI_CLUSTER not set")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if !netExists {
|
|
Packit |
63bb0d |
return nil, errors.New("GOVMOMI_NETWORK not set")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if !dsExists {
|
|
Packit |
63bb0d |
return nil, errors.New("GOVMOMI_DATASTORE not set")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if !folderExists {
|
|
Packit |
63bb0d |
return nil, errors.New("GOVMOMI_FOLDER not set")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return &AuthOptions{
|
|
Packit |
63bb0d |
Host: host,
|
|
Packit |
63bb0d |
Username: username,
|
|
Packit |
63bb0d |
Password: password,
|
|
Packit |
63bb0d |
Datacenter: datacenter,
|
|
Packit |
63bb0d |
Cluster: cluster,
|
|
Packit |
63bb0d |
Network: network,
|
|
Packit |
63bb0d |
Datastore: datastore,
|
|
Packit |
63bb0d |
Folder: folder,
|
|
Packit |
63bb0d |
}, nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func ImportImage(creds *AuthOptions, imagePath, imageName string) error {
|
|
Packit |
63bb0d |
args := []string{
|
|
Packit |
63bb0d |
"import.vmdk",
|
|
Packit |
63bb0d |
fmt.Sprintf("-u=%s:%s@%s", creds.Username, creds.Password, creds.Host),
|
|
Packit |
63bb0d |
"-k=true",
|
|
Packit |
63bb0d |
fmt.Sprintf("-dc=%s", creds.Datacenter),
|
|
Packit |
63bb0d |
fmt.Sprintf("-ds=%s", creds.Datastore),
|
|
Packit |
63bb0d |
imagePath,
|
|
Packit |
63bb0d |
imageName,
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
retcode := cli.Run(args)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if retcode != 0 {
|
|
Packit |
63bb0d |
return errors.New("importing vmdk failed")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func DeleteImage(creds *AuthOptions, directoryName string) error {
|
|
Packit |
63bb0d |
retcode := cli.Run([]string{
|
|
Packit |
63bb0d |
"datastore.rm",
|
|
Packit |
63bb0d |
"-f=true",
|
|
Packit |
63bb0d |
fmt.Sprintf("-u=%s:%s@%s", creds.Username, creds.Password, creds.Host),
|
|
Packit |
63bb0d |
"-k=true",
|
|
Packit |
63bb0d |
fmt.Sprintf("-dc=%s", creds.Datacenter),
|
|
Packit |
63bb0d |
fmt.Sprintf("-ds=%s", creds.Datastore),
|
|
Packit |
63bb0d |
directoryName + "*", // because vm.create creates another directory with _1 prefix
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if retcode != 0 {
|
|
Packit |
63bb0d |
return errors.New("deleting directory failed")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func runWithStdout(args []string) (string, int) {
|
|
Packit |
63bb0d |
oldStdout := os.Stdout
|
|
Packit |
63bb0d |
r, w, _ := os.Pipe()
|
|
Packit |
63bb0d |
os.Stdout = w
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
retcode := cli.Run(args)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
w.Close()
|
|
Packit |
63bb0d |
out, _ := ioutil.ReadAll(r)
|
|
Packit |
63bb0d |
os.Stdout = oldStdout
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return strings.TrimSpace(string(out)), retcode
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func WithBootedImage(creds *AuthOptions, imagePath, imageName, publicKey string, f func(address string) error) (retErr error) {
|
|
Packit |
63bb0d |
vmdkBaseName := filepath.Base(imagePath)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
args := []string{
|
|
Packit |
63bb0d |
"vm.create",
|
|
Packit |
63bb0d |
fmt.Sprintf("-u=%s:%s@%s", creds.Username, creds.Password, creds.Host),
|
|
Packit |
63bb0d |
"-k=true",
|
|
Packit |
63bb0d |
fmt.Sprintf("-dc=%s", creds.Datacenter),
|
|
Packit |
63bb0d |
fmt.Sprintf("-ds=%s", creds.Datastore),
|
|
Packit |
63bb0d |
fmt.Sprintf("-folder=%s", creds.Folder),
|
|
Packit |
63bb0d |
fmt.Sprintf("-net=%s", creds.Network),
|
|
Packit |
63bb0d |
"-m=2048", "-g=rhel8_64Guest", "-on=true", "-firmware=bios",
|
|
Packit |
63bb0d |
fmt.Sprintf("-disk=%s/%s", imageName, vmdkBaseName),
|
|
Packit |
63bb0d |
"--disk.controller=ide",
|
|
Packit |
63bb0d |
imageName,
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
retcode := cli.Run(args)
|
|
Packit |
63bb0d |
if retcode != 0 {
|
|
Packit |
63bb0d |
return errors.New("Creating VM from vmdk failed")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
defer func() {
|
|
Packit |
63bb0d |
args = []string{
|
|
Packit |
63bb0d |
"vm.destroy",
|
|
Packit |
63bb0d |
fmt.Sprintf("-u=%s:%s@%s", creds.Username, creds.Password, creds.Host),
|
|
Packit |
63bb0d |
"-k=true",
|
|
Packit |
63bb0d |
imageName,
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
retcode := cli.Run(args)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if retcode != 0 {
|
|
Packit |
63bb0d |
fmt.Printf("Deleting VM %s failed", imageName)
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// note: by default this will wait/block until an IP address is returned
|
|
Packit |
63bb0d |
// note: using exec() instead of running the command b/c .Run() returns an int
|
|
Packit |
63bb0d |
args = []string{
|
|
Packit |
63bb0d |
"vm.ip",
|
|
Packit |
63bb0d |
fmt.Sprintf("-u=%s:%s@%s", creds.Username, creds.Password, creds.Host),
|
|
Packit |
63bb0d |
"-k=true",
|
|
Packit |
63bb0d |
imageName,
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
ipAddress, retcode := runWithStdout(args)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if retcode != 0 {
|
|
Packit |
63bb0d |
return errors.New("Getting IP address for VM failed")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Disabled b/c of https://github.com/vmware/govmomi/issues/2054
|
|
Packit |
63bb0d |
// upload public key on the VM
|
|
Packit |
63bb0d |
//args = []string{
|
|
Packit |
63bb0d |
// "guest.mkdir",
|
|
Packit |
63bb0d |
// fmt.Sprintf("-u=%s:%s@%s", creds.Username, creds.Password, creds.Host),
|
|
Packit |
63bb0d |
// "-k=true",
|
|
Packit |
63bb0d |
// fmt.Sprintf("-vm=%s", imageName),
|
|
Packit |
63bb0d |
// "-p", "/root/.ssh",
|
|
Packit |
63bb0d |
//}
|
|
Packit |
63bb0d |
//retcode = cli.Run(args)
|
|
Packit |
63bb0d |
//if retcode != 0 {
|
|
Packit |
63bb0d |
// return errors.New("mkdir /root/.ssh on VM failed")
|
|
Packit |
63bb0d |
//}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
//args = []string{
|
|
Packit |
63bb0d |
// "guest.upload",
|
|
Packit |
63bb0d |
// fmt.Sprintf("-u=%s:%s@%s", creds.Username, creds.Password, creds.Host),
|
|
Packit |
63bb0d |
// "-k=true",
|
|
Packit |
63bb0d |
// fmt.Sprintf("-vm=%s", imageName),
|
|
Packit |
63bb0d |
// "-f=true",
|
|
Packit |
63bb0d |
// publicKey, // this is a file path
|
|
Packit |
63bb0d |
// "/root/.ssh/authorized_keys",
|
|
Packit |
63bb0d |
//}
|
|
Packit |
63bb0d |
//retcode = cli.Run(args)
|
|
Packit |
63bb0d |
//if retcode != 0 {
|
|
Packit |
63bb0d |
// return errors.New("Uploading public key to VM failed")
|
|
Packit |
63bb0d |
//}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return f(ipAddress)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// hard-coded SSH keys b/c we're having troubles uploading publicKey
|
|
Packit |
63bb0d |
// to the VM, see https://github.com/vmware/govmomi/issues/2054
|
|
Packit |
63bb0d |
func WithSSHKeyPair(f func(privateKey, publicKey string) error) error {
|
|
Packit |
63bb0d |
public := "/usr/share/tests/osbuild-composer/keyring/id_rsa.pub"
|
|
Packit |
63bb0d |
private := "/usr/share/tests/osbuild-composer/keyring/id_rsa"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return f(private, public)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func ConvertToStreamOptimizedVmdk(imagePath string) (string, error) {
|
|
Packit |
63bb0d |
optimizedVmdk, err := ioutil.TempFile("/var/tmp", "osbuild-composer-stream-optimized-*.vmdk")
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return "", err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
optimizedVmdk.Close()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
cmd := exec.Command(
|
|
Packit |
63bb0d |
"/usr/bin/qemu-img", "convert", "-O", "vmdk", "-o", "subformat=streamOptimized",
|
|
Packit |
63bb0d |
imagePath, optimizedVmdk.Name())
|
|
Packit |
63bb0d |
err = cmd.Run()
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return "", err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return optimizedVmdk.Name(), nil
|
|
Packit |
63bb0d |
}
|