diff --git a/config/external.go b/config/external.go index 06e977c2..4e2b55bb 100644 --- a/config/external.go +++ b/config/external.go @@ -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 == "" { diff --git a/config/external_test.go b/config/external_test.go new file mode 100644 index 00000000..29ff8ad3 --- /dev/null +++ b/config/external_test.go @@ -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) + } + }) + } +} diff --git a/config/lxd.go b/config/lxd.go index fe2604f6..2b4e0b36 100644 --- a/config/lxd.go +++ b/config/lxd.go @@ -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 { diff --git a/config/lxd_test.go b/config/lxd_test.go new file mode 100644 index 00000000..85d267d5 --- /dev/null +++ b/config/lxd_test.go @@ -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") +} diff --git a/database/sql/util.go b/database/sql/util.go index 970c59c8..454e7888 100644 --- a/database/sql/util.go +++ b/database/sql/util.go @@ -76,6 +76,10 @@ func (s *sqlDatabase) sqlToCommonOrganization(org Organization) params.Organizat Pools: make([]params.Pool, len(org.Pools)), } + for idx, pool := range org.Pools { + ret.Pools[idx] = s.sqlToCommonPool(pool) + } + return ret } diff --git a/go.mod b/go.mod index 6b1cc79b..3fdc3ce6 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module garm go 1.18 require ( - github.com/BurntSushi/toml v0.3.1 + github.com/BurntSushi/toml v0.4.1 github.com/go-resty/resty/v2 v2.7.0 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/go-github/v43 v43.0.0 @@ -17,11 +17,12 @@ require ( github.com/pkg/errors v0.9.1 github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b github.com/spf13/cobra v1.4.1-0.20220504202302-9e88759b19cd + github.com/stretchr/testify v1.7.5 golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 - golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.3.3 gorm.io/driver/sqlite v1.3.2 gorm.io/gorm v1.23.4 @@ -29,6 +30,7 @@ require ( require ( github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 // indirect github.com/go-macaroon-bakery/macaroonpb v1.0.0 // indirect @@ -43,10 +45,12 @@ require ( github.com/julienschmidt/httprouter v1.3.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kr/fs v0.1.0 // indirect + github.com/kr/pretty v0.3.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-sqlite3 v1.14.12 // indirect github.com/pborman/uuid v1.2.1 // indirect github.com/pkg/sftp v1.13.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/fastuuid v1.2.0 // indirect @@ -56,6 +60,7 @@ require ( golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/errgo.v1 v1.0.1 // indirect gopkg.in/httprequest.v1 v1.2.1 // indirect gopkg.in/macaroon-bakery.v2 v2.3.0 // indirect diff --git a/go.sum b/go.sum index 64c0134f..a941cdb7 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,9 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= @@ -174,8 +175,9 @@ github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -192,7 +194,6 @@ github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= @@ -212,6 +213,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -222,11 +225,14 @@ github.com/spf13/cobra v1.4.1-0.20220504202302-9e88759b19cd/go.mod h1:Wo4iy3BUC+ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= +github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -312,8 +318,8 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -496,8 +502,9 @@ google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscL google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk= gopkg.in/errgo.v1 v1.0.1 h1:oQFRXzZ7CkBGdm1XZm/EbQYaYNNEElNBOd09M6cqNso= gopkg.in/errgo.v1 v1.0.1/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo= @@ -521,8 +528,8 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8= gorm.io/driver/mysql v1.3.3/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= gorm.io/driver/sqlite v1.3.2 h1:nWTy4cE52K6nnMhv23wLmur9Y3qWbZvOBz+V4PrGAxg= diff --git a/runner/providers/lxd/images.go b/runner/providers/lxd/images.go index 6c527996..3a3fa4e2 100644 --- a/runner/providers/lxd/images.go +++ b/runner/providers/lxd/images.go @@ -29,16 +29,16 @@ import ( ) type image struct { - remotes map[string]config.LXDRemote + remotes map[string]config.LXDImageRemote cli lxd.InstanceServer } // parseImageName parses the image name that comes in from the config and returns a // remote. If no remote is configured with the given name, an error is returned. -func (i *image) parseImageName(imageName string) (config.LXDRemote, string, error) { +func (i *image) parseImageName(imageName string) (config.LXDImageRemote, string, error) { if !strings.Contains(imageName, ":") { - return config.LXDRemote{}, "", fmt.Errorf("image does not include a remote") + return config.LXDImageRemote{}, "", fmt.Errorf("image does not include a remote") } details := strings.SplitN(imageName, ":", 2) @@ -47,7 +47,7 @@ func (i *image) parseImageName(imageName string) (config.LXDRemote, string, erro return val, details[1], nil } } - return config.LXDRemote{}, "", runnerErrors.ErrNotFound + return config.LXDImageRemote{}, "", runnerErrors.ErrNotFound } func (i *image) getLocalImageByAlias(imageName string, imageType config.LXDImageType, arch string) (*api.Image, error) { @@ -68,7 +68,7 @@ func (i *image) getLocalImageByAlias(imageName string, imageType config.LXDImage return image, nil } -func (i *image) clientFromRemoteArgs(remote config.LXDRemote) (lxd.ImageServer, error) { +func (i *image) clientFromRemoteArgs(remote config.LXDImageRemote) (lxd.ImageServer, error) { connectArgs := &lxd.ConnectionArgs{ InsecureSkipVerify: remote.InsecureSkipVerify, } @@ -79,7 +79,7 @@ func (i *image) clientFromRemoteArgs(remote config.LXDRemote) (lxd.ImageServer, return d, nil } -func (i *image) copyImageFromRemote(remote config.LXDRemote, imageName string, imageType config.LXDImageType, arch string) (*api.Image, error) { +func (i *image) copyImageFromRemote(remote config.LXDImageRemote, imageName string, imageType config.LXDImageType, arch string) (*api.Image, error) { imgCli, err := i.clientFromRemoteArgs(remote) if err != nil { return nil, errors.Wrap(err, "fetching image server client") diff --git a/runner/providers/lxd/lxd.go b/runner/providers/lxd/lxd.go index 7d8cb119..a80933de 100644 --- a/runner/providers/lxd/lxd.go +++ b/runner/providers/lxd/lxd.go @@ -325,7 +325,12 @@ func (l *LXD) DeleteInstance(ctx context.Context, instance string) error { if isNotFoundError(err) { return nil } - return errors.Wrap(err, "stopping instance") + // I am not proud of this, but the drivers.ErrInstanceIsStopped from LXD pulls in + // a ton of CGO, linux specific dependencies, that don't make sense having + // in garm. + if !(err.Error() == errInstanceIsStopped.Error()) { + return errors.Wrap(err, "stopping instance") + } } op, err := l.cli.DeleteInstance(instance) diff --git a/runner/providers/lxd/util.go b/runner/providers/lxd/util.go index 446c5e0a..686531ec 100644 --- a/runner/providers/lxd/util.go +++ b/runner/providers/lxd/util.go @@ -34,6 +34,10 @@ import ( "github.com/pkg/errors" ) +var ( + errInstanceIsStopped error = fmt.Errorf("The instance is already stopped") +) + var httpResponseErrors = map[int][]error{ http.StatusNotFound: {os.ErrNotExist, sql.ErrNoRows}, } diff --git a/testdata/lxd/certs/client.crt b/testdata/lxd/certs/client.crt new file mode 100644 index 00000000..0116d129 --- /dev/null +++ b/testdata/lxd/certs/client.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1TCCAVqgAwIBAgIQNhEBo+jy+KEPV75uOd/sDjAKBggqhkjOPQQDAzAyMRww +GgYDVQQKExNsaW51eGNvbnRhaW5lcnMub3JnMRIwEAYDVQQDDAlyb290QGdhcm0w +HhcNMjIwNjI3MTYxODQyWhcNMzIwNjI0MTYxODQyWjAyMRwwGgYDVQQKExNsaW51 +eGNvbnRhaW5lcnMub3JnMRIwEAYDVQQDDAlyb290QGdhcm0wdjAQBgcqhkjOPQIB +BgUrgQQAIgNiAASN1R6Bg+AwuEx4imM2uzIK+3wXwxMZZ52rdDbsgUjE+Dtm2e0M +ZFkMNznizme7TtOnzaHlw3N6+XQv2M/DkzqYAUjeoMfY3XnPaQnuZA7Kzi+NXfpM +KMXqoYoHN9WAYgKjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEF +BQcDAjAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMDA2kAMGYCMQCK8akNYXopJbPo +Vjiuc9u0xdBGqX9ImteHSD3KzC11XRYuDUWqSTr829rM/2l4x3ICMQCrhwvNnLfP +LI9ABEWRXawUMBTFAKuZDLBIWlkGH/I2oBmiXIhISwPh3qGfzQZs5e0= +-----END CERTIFICATE----- diff --git a/testdata/lxd/certs/client.key b/testdata/lxd/certs/client.key new file mode 100644 index 00000000..26a7a153 --- /dev/null +++ b/testdata/lxd/certs/client.key @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDBZAwg8N7axFKRBhpC7BejyBf99YBF+NyxowKWbWwBjUZdJ51fO8wMV +QnNtoqQxLmigBwYFK4EEACKhZANiAASN1R6Bg+AwuEx4imM2uzIK+3wXwxMZZ52r +dDbsgUjE+Dtm2e0MZFkMNznizme7TtOnzaHlw3N6+XQv2M/DkzqYAUjeoMfY3XnP +aQnuZA7Kzi+NXfpMKMXqoYoHN9WAYgI= +-----END EC PRIVATE KEY----- diff --git a/testdata/lxd/certs/servercert.crt b/testdata/lxd/certs/servercert.crt new file mode 100644 index 00000000..9e2f253b --- /dev/null +++ b/testdata/lxd/certs/servercert.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICLTCCAbOgAwIBAgIQfcT7OO+Gn2Q4kEsQstUltTAKBggqhkjOPQQDAzBCMRww +GgYDVQQKExNsaW51eGNvbnRhaW5lcnMub3JnMSIwIAYDVQQDDBlyb290QGNsb3Vk +YmFzZS03WDMzQTAwOE5BMB4XDTIyMDYxNDEzNTMzM1oXDTMyMDYxMTEzNTMzM1ow +QjEcMBoGA1UEChMTbGludXhjb250YWluZXJzLm9yZzEiMCAGA1UEAwwZcm9vdEBj +bG91ZGJhc2UtN1gzM0EwMDhOQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMkPig4N +ZTi8RQF+MYY8yVnw6Smmv6ha2Hp3Kp+j1qu9Wm4MKoMasFqJr/r/A2cv+iUrHTZE +pPEbTSEjCrulSz8H+myf/Z4LQ5pXxasVpnPqQnIvkchm452qNtibmhykNqNuMGww +DgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQC +MAAwNwYDVR0RBDAwLoIUY2xvdWRiYXNlLTdYMzNBMDA4TkGHBH8AAAGHEAAAAAAA +AAAAAAAAAAAAAAEwCgYIKoZIzj0EAwMDaAAwZQIwEnn9OA0R6VGTxmj+4AJm87co +/+SfLchTUESeoAqdJLdNfVdzE1mbp4WBgdq8tPdGAjEArjO0QzLey5f4p6tucJEy +nXkN3KSeRAdzM05OYTYF/u0lGRq0XjJYTSfxzOSWhyYQ +-----END CERTIFICATE-----