Add idempotency when stopping a VM and some tests
When deleting a VM, we try to force stop it. If the VM is already stopped, LXD will return an error. Unfortunately, we can't import the drivers package from LXD without also pulling in a bunch of linux specific CGO dependencies which we want to avoid. Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
parent
b4e9af13d5
commit
f52accc47f
13 changed files with 340 additions and 27 deletions
|
|
@ -36,12 +36,12 @@ func (e *External) ExecutablePath() (string, error) {
|
|||
|
||||
func (e *External) Validate() error {
|
||||
if e.ConfigFile != "" {
|
||||
if _, err := os.Stat(e.ConfigFile); err != nil {
|
||||
return fmt.Errorf("failed to access config file %s", e.ConfigFile)
|
||||
}
|
||||
if !filepath.IsAbs(e.ConfigFile) {
|
||||
return fmt.Errorf("path to config file must be an absolute path")
|
||||
}
|
||||
if _, err := os.Stat(e.ConfigFile); err != nil {
|
||||
return fmt.Errorf("failed to access config file %s", e.ConfigFile)
|
||||
}
|
||||
}
|
||||
|
||||
if e.ProviderDir == "" {
|
||||
|
|
|
|||
88
config/external_test.go
Normal file
88
config/external_test.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getDefaultExternalConfig(t *testing.T) External {
|
||||
dir, err := ioutil.TempDir("", "garm-test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temporary directory: %s", err)
|
||||
}
|
||||
t.Cleanup(func() { os.RemoveAll(dir) })
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(dir, "garm-external-provider"), []byte{}, 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write file: %s", err)
|
||||
}
|
||||
|
||||
return External{
|
||||
ConfigFile: "",
|
||||
ProviderDir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
func TestExternal(t *testing.T) {
|
||||
cfg := getDefaultExternalConfig(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg External
|
||||
errString string
|
||||
}{
|
||||
{
|
||||
name: "Config is valid",
|
||||
cfg: cfg,
|
||||
errString: "",
|
||||
},
|
||||
{
|
||||
name: "Config path cannot be relative path",
|
||||
cfg: External{
|
||||
ConfigFile: "../test",
|
||||
ProviderDir: cfg.ProviderDir,
|
||||
},
|
||||
errString: "path to config file must be an absolute path",
|
||||
},
|
||||
{
|
||||
name: "Config must exist if specified",
|
||||
cfg: External{
|
||||
ConfigFile: "/there/is/no/config/here",
|
||||
ProviderDir: cfg.ProviderDir,
|
||||
},
|
||||
errString: "failed to access config file /there/is/no/config/here",
|
||||
},
|
||||
{
|
||||
name: "Missing provider dir",
|
||||
cfg: External{
|
||||
ConfigFile: "",
|
||||
ProviderDir: "",
|
||||
},
|
||||
errString: "missing provider dir",
|
||||
},
|
||||
{
|
||||
name: "Provider dir must not be relative",
|
||||
cfg: External{
|
||||
ConfigFile: "",
|
||||
ProviderDir: "../test",
|
||||
},
|
||||
errString: "path to provider dir must be absolute",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.cfg.Validate()
|
||||
if tc.errString == "" {
|
||||
assert.Nil(t, err)
|
||||
} else {
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, tc.errString)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -35,14 +35,16 @@ const (
|
|||
LXDImageContainer LXDImageType = "container"
|
||||
)
|
||||
|
||||
type LXDRemote struct {
|
||||
// LXDImageRemote holds information about a remote server from which LXD can fetch
|
||||
// OS images. Typically this will be a simplestreams server.
|
||||
type LXDImageRemote struct {
|
||||
Address string `toml:"addr" json:"addr"`
|
||||
Public bool `toml:"public" json:"public"`
|
||||
Protocol LXDRemoteProtocol `toml:"protocol" json:"protocol"`
|
||||
InsecureSkipVerify bool `toml:"skip_verify" json:"skip-verify"`
|
||||
}
|
||||
|
||||
func (l *LXDRemote) Validate() error {
|
||||
func (l *LXDImageRemote) Validate() error {
|
||||
if l.Protocol != SimpleStreams {
|
||||
// Only supports simplestreams for now.
|
||||
return fmt.Errorf("invalid remote protocol %s. Supported protocols: %s", l.Protocol, SimpleStreams)
|
||||
|
|
@ -94,7 +96,7 @@ type LXD struct {
|
|||
|
||||
// ImageRemotes is a map to a set of remote image repositories we can use to
|
||||
// download images.
|
||||
ImageRemotes map[string]LXDRemote `toml:"image_remotes" json:"image-remotes"`
|
||||
ImageRemotes map[string]LXDImageRemote `toml:"image_remotes" json:"image-remotes"`
|
||||
|
||||
// SecureBoot enables secure boot for VMs spun up using this provider.
|
||||
SecureBoot bool `yaml:"secure_boot" json:"secure-boot"`
|
||||
|
|
@ -113,12 +115,17 @@ func (l *LXD) Validate() error {
|
|||
return fmt.Errorf("unix_socket or address must be specified")
|
||||
}
|
||||
|
||||
if _, err := url.Parse(l.URL); err != nil {
|
||||
url, err := url.ParseRequestURI(l.URL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid LXD URL")
|
||||
}
|
||||
|
||||
if url.Scheme != "https" {
|
||||
return fmt.Errorf("address must be https")
|
||||
}
|
||||
|
||||
if l.ClientCertificate == "" || l.ClientKey == "" {
|
||||
return fmt.Errorf("client_certificate and client_key are mandatory when connecting via HTTPs")
|
||||
return fmt.Errorf("client_certificate and client_key are mandatory")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(l.ClientCertificate); err != nil {
|
||||
|
|
|
|||
161
config/lxd_test.go
Normal file
161
config/lxd_test.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getDefaultLXDImageRemoteConfig() LXDImageRemote {
|
||||
return LXDImageRemote{
|
||||
Address: "https://cloud-images.ubuntu.com/releases",
|
||||
Public: true,
|
||||
Protocol: SimpleStreams,
|
||||
InsecureSkipVerify: false,
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultLXDConfig() LXD {
|
||||
remote := getDefaultLXDImageRemoteConfig()
|
||||
return LXD{
|
||||
URL: "https://example.com:8443",
|
||||
ProjectName: "default",
|
||||
IncludeDefaultProfile: false,
|
||||
ClientCertificate: "../testdata/lxd/certs/client.crt",
|
||||
ClientKey: "../testdata/lxd/certs/client.key",
|
||||
TLSServerCert: "../testdata/lxd/certs/servercert.crt",
|
||||
ImageRemotes: map[string]LXDImageRemote{
|
||||
"default": remote,
|
||||
},
|
||||
SecureBoot: false,
|
||||
}
|
||||
}
|
||||
|
||||
func TestLXDRemote(t *testing.T) {
|
||||
cfg := getDefaultLXDImageRemoteConfig()
|
||||
|
||||
err := cfg.Validate()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestLXDRemoteEmptyAddress(t *testing.T) {
|
||||
cfg := getDefaultLXDImageRemoteConfig()
|
||||
|
||||
cfg.Address = ""
|
||||
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "missing address")
|
||||
}
|
||||
|
||||
func TestLXDRemoteInvalidAddress(t *testing.T) {
|
||||
cfg := getDefaultLXDImageRemoteConfig()
|
||||
|
||||
cfg.Address = "bogus address"
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "validating address: parse \"bogus address\": invalid URI for request")
|
||||
}
|
||||
|
||||
func TestLXDRemoteIvalidAddressScheme(t *testing.T) {
|
||||
cfg := getDefaultLXDImageRemoteConfig()
|
||||
|
||||
cfg.Address = "ftp://whatever"
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "address must be http or https")
|
||||
}
|
||||
|
||||
func TestLXDConfig(t *testing.T) {
|
||||
cfg := getDefaultLXDConfig()
|
||||
err := cfg.Validate()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestLXDWithInvalidUnixSocket(t *testing.T) {
|
||||
cfg := getDefaultLXDConfig()
|
||||
|
||||
cfg.UnixSocket = "bogus unix socket"
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "could not access unix socket bogus unix socket: \"stat bogus unix socket: no such file or directory\"")
|
||||
}
|
||||
|
||||
func TestMissingUnixSocketAndMissingURL(t *testing.T) {
|
||||
cfg := getDefaultLXDConfig()
|
||||
|
||||
cfg.URL = ""
|
||||
cfg.UnixSocket = ""
|
||||
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "unix_socket or address must be specified")
|
||||
}
|
||||
|
||||
func TestInvalidLXDURL(t *testing.T) {
|
||||
cfg := getDefaultLXDConfig()
|
||||
cfg.URL = "bogus"
|
||||
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "invalid LXD URL")
|
||||
}
|
||||
|
||||
func TestLXDURLIsHTTPS(t *testing.T) {
|
||||
cfg := getDefaultLXDConfig()
|
||||
cfg.URL = "http://example.com"
|
||||
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "address must be https")
|
||||
}
|
||||
|
||||
func TestMissingClientCertOrKey(t *testing.T) {
|
||||
cfg := getDefaultLXDConfig()
|
||||
cfg.ClientKey = ""
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "client_certificate and client_key are mandatory")
|
||||
|
||||
cfg = getDefaultLXDConfig()
|
||||
cfg.ClientCertificate = ""
|
||||
err = cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "client_certificate and client_key are mandatory")
|
||||
}
|
||||
|
||||
func TestLXDIvalidCertOrKeyPaths(t *testing.T) {
|
||||
cfg := getDefaultLXDConfig()
|
||||
cfg.ClientCertificate = "/i/am/not/here"
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "failed to access client certificate /i/am/not/here: \"stat /i/am/not/here: no such file or directory\"")
|
||||
|
||||
cfg.ClientCertificate = "../testdata/lxd/certs/client.crt"
|
||||
cfg.ClientKey = "/me/neither"
|
||||
|
||||
err = cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "failed to access client key /me/neither: \"stat /me/neither: no such file or directory\"")
|
||||
}
|
||||
|
||||
func TestLXDInvalidServerCertPath(t *testing.T) {
|
||||
cfg := getDefaultLXDConfig()
|
||||
cfg.TLSServerCert = "/not/a/valid/server/cert/path"
|
||||
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "failed to access tls_server_certificate /not/a/valid/server/cert/path: \"stat /not/a/valid/server/cert/path: no such file or directory\"")
|
||||
}
|
||||
|
||||
func TestInvalidLXDImageRemotes(t *testing.T) {
|
||||
cfg := getDefaultLXDConfig()
|
||||
|
||||
cfg.ImageRemotes["default"] = LXDImageRemote{
|
||||
Protocol: LXDRemoteProtocol("bogus"),
|
||||
}
|
||||
|
||||
err := cfg.Validate()
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualError(t, err, "remote default is invalid: invalid remote protocol bogus. Supported protocols: simplestreams")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue