github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/lxd/provider_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lxd_test
     5  
     6  import (
     7  	stdcontext "context"
     8  	"fmt"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"path"
    12  	"strings"
    13  
    14  	"github.com/juju/errors"
    15  	gitjujutesting "github.com/juju/testing"
    16  	jc "github.com/juju/testing/checkers"
    17  	"github.com/juju/utils/v3"
    18  	"go.uber.org/mock/gomock"
    19  	gc "gopkg.in/check.v1"
    20  	"gopkg.in/yaml.v2"
    21  
    22  	"github.com/juju/juju/cloud"
    23  	"github.com/juju/juju/environs"
    24  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    25  	"github.com/juju/juju/environs/context"
    26  	"github.com/juju/juju/environs/testing"
    27  	"github.com/juju/juju/juju/osenv"
    28  	"github.com/juju/juju/provider/lxd"
    29  	"github.com/juju/juju/provider/lxd/lxdnames"
    30  	jujutesting "github.com/juju/juju/testing"
    31  )
    32  
    33  var (
    34  	_ = gc.Suite(&providerSuite{})
    35  	_ = gc.Suite(&ProviderFunctionalSuite{})
    36  )
    37  
    38  type providerSuite struct {
    39  	lxd.BaseSuite
    40  }
    41  
    42  func (s *providerSuite) SetUpTest(c *gc.C) {
    43  	s.BaseSuite.SetUpTest(c)
    44  }
    45  
    46  type providerSuiteDeps struct {
    47  	provider      environs.EnvironProvider
    48  	creds         *testing.MockProviderCredentials
    49  	credsRegister *testing.MockProviderCredentialsRegister
    50  	factory       *lxd.MockServerFactory
    51  	configReader  *lxd.MockLXCConfigReader
    52  }
    53  
    54  func (s *providerSuite) createProvider(ctrl *gomock.Controller) providerSuiteDeps {
    55  	creds := testing.NewMockProviderCredentials(ctrl)
    56  	credsRegister := testing.NewMockProviderCredentialsRegister(ctrl)
    57  	factory := lxd.NewMockServerFactory(ctrl)
    58  	configReader := lxd.NewMockLXCConfigReader(ctrl)
    59  
    60  	provider := lxd.NewProviderWithMocks(creds, credsRegister, factory, configReader)
    61  	return providerSuiteDeps{
    62  		provider:      provider,
    63  		creds:         creds,
    64  		credsRegister: credsRegister,
    65  		factory:       factory,
    66  		configReader:  configReader,
    67  	}
    68  }
    69  
    70  func (s *providerSuite) TestDetectClouds(c *gc.C) {
    71  	ctrl := gomock.NewController(c)
    72  	defer ctrl.Finish()
    73  
    74  	deps := s.createProvider(ctrl)
    75  	deps.configReader.EXPECT().ReadConfig(path.Join(osenv.JujuXDGDataHomePath("lxd"), "config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
    76  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), ".config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
    77  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/current/.config/lxc/config.yml")).Return(lxd.LXCConfig{}, nil)
    78  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/common/config/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
    79  
    80  	cloudDetector := deps.provider.(environs.CloudDetector)
    81  
    82  	clouds, err := cloudDetector.DetectClouds()
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	c.Assert(clouds, gc.HasLen, 1)
    85  	s.assertLocalhostCloud(c, clouds[0])
    86  }
    87  
    88  func (s *providerSuite) TestRemoteDetectClouds(c *gc.C) {
    89  	ctrl := gomock.NewController(c)
    90  	defer ctrl.Finish()
    91  
    92  	deps := s.createProvider(ctrl)
    93  
    94  	deps.configReader.EXPECT().ReadConfig(path.Join(osenv.JujuXDGDataHomePath("lxd"), "config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
    95  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), ".config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
    96  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/current/.config/lxc/config.yml")).Return(lxd.LXCConfig{
    97  		DefaultRemote: "localhost",
    98  		Remotes: map[string]lxd.LXCRemoteConfig{
    99  			"nuc1": {
   100  				Addr:     "https://10.0.0.1:8443",
   101  				AuthType: "certificate",
   102  				Protocol: "lxd",
   103  				Public:   false,
   104  			},
   105  		},
   106  	}, nil)
   107  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/common/config/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   108  
   109  	cloudDetector := deps.provider.(environs.CloudDetector)
   110  
   111  	clouds, err := cloudDetector.DetectClouds()
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	c.Assert(clouds, gc.HasLen, 2)
   114  	c.Assert(clouds, jc.DeepEquals, []cloud.Cloud{
   115  		{
   116  			Name: "localhost",
   117  			Type: "lxd",
   118  			AuthTypes: []cloud.AuthType{
   119  				cloud.CertificateAuthType,
   120  			},
   121  			Regions: []cloud.Region{{
   122  				Name: "localhost",
   123  			}},
   124  			Description: "LXD Container Hypervisor",
   125  		},
   126  		{
   127  			Name:     "nuc1",
   128  			Type:     "lxd",
   129  			Endpoint: "https://10.0.0.1:8443",
   130  			AuthTypes: []cloud.AuthType{
   131  				cloud.CertificateAuthType,
   132  			},
   133  			Regions: []cloud.Region{{
   134  				Name:     "default",
   135  				Endpoint: "https://10.0.0.1:8443",
   136  			}},
   137  			Description: "LXD Cluster",
   138  		},
   139  	})
   140  }
   141  
   142  func (s *providerSuite) TestRemoteDetectCloudsWithConfigError(c *gc.C) {
   143  	ctrl := gomock.NewController(c)
   144  	defer ctrl.Finish()
   145  
   146  	deps := s.createProvider(ctrl)
   147  	deps.configReader.EXPECT().ReadConfig(path.Join(osenv.JujuXDGDataHomePath("lxd"), "config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   148  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), ".config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   149  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/current/.config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   150  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/common/config/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   151  
   152  	cloudDetector := deps.provider.(environs.CloudDetector)
   153  
   154  	clouds, err := cloudDetector.DetectClouds()
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	c.Assert(clouds, gc.HasLen, 1)
   157  	s.assertLocalhostCloud(c, clouds[0])
   158  }
   159  
   160  func (s *providerSuite) TestDetectCloud(c *gc.C) {
   161  	ctrl := gomock.NewController(c)
   162  	defer ctrl.Finish()
   163  
   164  	deps := s.createProvider(ctrl)
   165  	deps.configReader.EXPECT().ReadConfig(path.Join(osenv.JujuXDGDataHomePath("lxd"), "config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   166  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), ".config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   167  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/current/.config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   168  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/common/config/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   169  	deps.configReader.EXPECT().ReadConfig(path.Join(osenv.JujuXDGDataHomePath("lxd"), "config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   170  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), ".config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   171  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/current/.config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   172  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/common/config/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   173  	cloudDetector := deps.provider.(environs.CloudDetector)
   174  
   175  	cloud, err := cloudDetector.DetectCloud("localhost")
   176  	c.Assert(err, jc.ErrorIsNil)
   177  	s.assertLocalhostCloud(c, cloud)
   178  	cloud, err = cloudDetector.DetectCloud("lxd")
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	s.assertLocalhostCloud(c, cloud)
   181  }
   182  
   183  func (s *providerSuite) TestRemoteDetectCloud(c *gc.C) {
   184  	ctrl := gomock.NewController(c)
   185  	defer ctrl.Finish()
   186  
   187  	deps := s.createProvider(ctrl)
   188  	cloudDetector := deps.provider.(environs.CloudDetector)
   189  
   190  	deps.configReader.EXPECT().ReadConfig(path.Join(osenv.JujuXDGDataHomePath("lxd"), "config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   191  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), ".config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   192  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/common/config/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   193  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/current/.config/lxc/config.yml")).Return(lxd.LXCConfig{
   194  		DefaultRemote: "localhost",
   195  		Remotes: map[string]lxd.LXCRemoteConfig{
   196  			"nuc1": {
   197  				Addr:     "https://10.0.0.1:8443",
   198  				AuthType: "certificate",
   199  				Protocol: "lxd",
   200  				Public:   false,
   201  			},
   202  		},
   203  	}, nil)
   204  
   205  	got, err := cloudDetector.DetectCloud("nuc1")
   206  	c.Assert(err, jc.ErrorIsNil)
   207  	c.Assert(got, jc.DeepEquals, cloud.Cloud{
   208  		Name:     "nuc1",
   209  		Type:     "lxd",
   210  		Endpoint: "https://10.0.0.1:8443",
   211  		AuthTypes: []cloud.AuthType{
   212  			cloud.CertificateAuthType,
   213  		},
   214  		Regions: []cloud.Region{{
   215  			Name:     "default",
   216  			Endpoint: "https://10.0.0.1:8443",
   217  		}},
   218  		Description: "LXD Cluster",
   219  	})
   220  }
   221  
   222  func (s *providerSuite) TestRemoteDetectCloudWithConfigError(c *gc.C) {
   223  	ctrl := gomock.NewController(c)
   224  	defer ctrl.Finish()
   225  
   226  	deps := s.createProvider(ctrl)
   227  	cloudDetector := deps.provider.(environs.CloudDetector)
   228  
   229  	deps.configReader.EXPECT().ReadConfig(path.Join(osenv.JujuXDGDataHomePath("lxd"), "config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   230  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), ".config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   231  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/current/.config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   232  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/common/config/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   233  
   234  	_, err := cloudDetector.DetectCloud("nuc1")
   235  	c.Assert(err, gc.ErrorMatches, `cloud nuc1 not found`)
   236  }
   237  
   238  func (s *providerSuite) TestDetectCloudError(c *gc.C) {
   239  	ctrl := gomock.NewController(c)
   240  	defer ctrl.Finish()
   241  
   242  	deps := s.createProvider(ctrl)
   243  	deps.configReader.EXPECT().ReadConfig(path.Join(osenv.JujuXDGDataHomePath("lxd"), "config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   244  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), ".config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   245  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/current/.config/lxc/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   246  	deps.configReader.EXPECT().ReadConfig(path.Join(utils.Home(), "snap/lxd/common/config/config.yml")).Return(lxd.LXCConfig{}, errors.New("bad"))
   247  
   248  	cloudDetector := deps.provider.(environs.CloudDetector)
   249  
   250  	_, err := cloudDetector.DetectCloud("foo")
   251  	c.Assert(err, gc.ErrorMatches, `cloud foo not found`)
   252  }
   253  
   254  func (s *providerSuite) assertLocalhostCloud(c *gc.C, found cloud.Cloud) {
   255  	c.Assert(found, jc.DeepEquals, cloud.Cloud{
   256  		Name: "localhost",
   257  		Type: "lxd",
   258  		AuthTypes: []cloud.AuthType{
   259  			cloud.CertificateAuthType,
   260  		},
   261  		Regions: []cloud.Region{{
   262  			Name: "localhost",
   263  		}},
   264  		Description: "LXD Container Hypervisor",
   265  	})
   266  }
   267  
   268  func (s *providerSuite) TestFinalizeCloud(c *gc.C) {
   269  	ctrl := gomock.NewController(c)
   270  	defer ctrl.Finish()
   271  
   272  	deps := s.createProvider(ctrl)
   273  	server := lxd.NewMockServer(ctrl)
   274  	finalizer := deps.provider.(environs.CloudFinalizer)
   275  
   276  	deps.factory.EXPECT().LocalServer().Return(server, nil)
   277  	server.EXPECT().LocalBridgeName().Return("lxdbr0")
   278  	deps.factory.EXPECT().LocalServerAddress().Return("1.2.3.4:1234", nil)
   279  
   280  	var ctx mockContext
   281  	out, err := finalizer.FinalizeCloud(&ctx, cloud.Cloud{
   282  		Name:      "localhost",
   283  		Type:      "lxd",
   284  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   285  		Regions: []cloud.Region{{
   286  			Name: "localhost",
   287  		}},
   288  	})
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	c.Assert(out, jc.DeepEquals, cloud.Cloud{
   291  		Name:      "localhost",
   292  		Type:      "lxd",
   293  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   294  		Endpoint:  "1.2.3.4:1234",
   295  		Regions: []cloud.Region{{
   296  			Name:     "localhost",
   297  			Endpoint: "1.2.3.4:1234",
   298  		}},
   299  	})
   300  	ctx.CheckCallNames(c, "Verbosef")
   301  	ctx.CheckCall(
   302  		c, 0, "Verbosef", "Resolved LXD host address on bridge %s: %s",
   303  		[]interface{}{"lxdbr0", "1.2.3.4:1234"},
   304  	)
   305  }
   306  
   307  func (s *providerSuite) TestFinalizeCloudWithRemoteProvider(c *gc.C) {
   308  	ctrl := gomock.NewController(c)
   309  	defer ctrl.Finish()
   310  
   311  	deps := s.createProvider(ctrl)
   312  	finalizer := deps.provider.(environs.CloudFinalizer)
   313  
   314  	var ctx mockContext
   315  	out, err := finalizer.FinalizeCloud(&ctx, cloud.Cloud{
   316  		Name:      "nuc8",
   317  		Type:      "lxd",
   318  		Endpoint:  "http://10.0.0.1:8443",
   319  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   320  		Regions:   []cloud.Region{},
   321  	})
   322  	c.Assert(err, jc.ErrorIsNil)
   323  	c.Assert(out, jc.DeepEquals, cloud.Cloud{
   324  		Name:      "nuc8",
   325  		Type:      "lxd",
   326  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   327  		Endpoint:  "http://10.0.0.1:8443",
   328  		Regions: []cloud.Region{{
   329  			Name:     "default",
   330  			Endpoint: "http://10.0.0.1:8443",
   331  		}},
   332  	})
   333  }
   334  
   335  func (s *providerSuite) TestFinalizeCloudWithRemoteProviderWithOnlyRegionEndpoint(c *gc.C) {
   336  	ctrl := gomock.NewController(c)
   337  	defer ctrl.Finish()
   338  
   339  	deps := s.createProvider(ctrl)
   340  	cloudFinalizer := deps.provider.(environs.CloudFinalizer)
   341  
   342  	cloudSpec := cloud.Cloud{
   343  		Name:      "foo",
   344  		Type:      "lxd",
   345  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   346  		Regions: []cloud.Region{{
   347  			Name:     "bar",
   348  			Endpoint: "https://321.321.12.12",
   349  		}},
   350  	}
   351  
   352  	ctx := testing.NewMockFinalizeCloudContext(ctrl)
   353  	got, err := cloudFinalizer.FinalizeCloud(ctx, cloudSpec)
   354  	c.Assert(err, jc.ErrorIsNil)
   355  	c.Assert(got, gc.DeepEquals, cloudSpec)
   356  }
   357  
   358  func (s *providerSuite) TestFinalizeCloudWithRemoteProviderWithMixedRegions(c *gc.C) {
   359  	ctrl := gomock.NewController(c)
   360  	defer ctrl.Finish()
   361  
   362  	deps := s.createProvider(ctrl)
   363  	cloudFinalizer := deps.provider.(environs.CloudFinalizer)
   364  
   365  	server := lxd.NewMockServer(ctrl)
   366  
   367  	deps.factory.EXPECT().LocalServer().Return(server, nil)
   368  	server.EXPECT().LocalBridgeName().Return("lxdbr0")
   369  	deps.factory.EXPECT().LocalServerAddress().Return("https://192.0.0.1:8443", nil)
   370  
   371  	cloudSpec := cloud.Cloud{
   372  		Name:      "localhost",
   373  		Type:      "lxd",
   374  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   375  		Regions: []cloud.Region{{
   376  			Name:     "bar",
   377  			Endpoint: "https://321.321.12.12",
   378  		}},
   379  	}
   380  
   381  	ctx := testing.NewMockFinalizeCloudContext(ctrl)
   382  	ctx.EXPECT().Verbosef("Resolved LXD host address on bridge %s: %s", "lxdbr0", "https://192.0.0.1:8443")
   383  
   384  	got, err := cloudFinalizer.FinalizeCloud(ctx, cloudSpec)
   385  	c.Assert(err, jc.ErrorIsNil)
   386  	c.Assert(got, gc.DeepEquals, cloud.Cloud{
   387  		Name:      "localhost",
   388  		Type:      "lxd",
   389  		Endpoint:  "https://192.0.0.1:8443",
   390  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   391  		Regions: []cloud.Region{{
   392  			Name:     "bar",
   393  			Endpoint: "https://321.321.12.12",
   394  		}},
   395  	})
   396  }
   397  
   398  func (s *providerSuite) TestFinalizeCloudWithRemoteProviderWithNoRegion(c *gc.C) {
   399  	ctrl := gomock.NewController(c)
   400  	defer ctrl.Finish()
   401  
   402  	deps := s.createProvider(ctrl)
   403  	cloudFinalizer := deps.provider.(environs.CloudFinalizer)
   404  
   405  	cloudSpec := cloud.Cloud{
   406  		Name:      "test",
   407  		Type:      "lxd",
   408  		Endpoint:  "https://192.0.0.1:8443",
   409  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   410  		Regions:   []cloud.Region{},
   411  	}
   412  
   413  	ctx := testing.NewMockFinalizeCloudContext(ctrl)
   414  
   415  	got, err := cloudFinalizer.FinalizeCloud(ctx, cloudSpec)
   416  	c.Assert(err, jc.ErrorIsNil)
   417  	c.Assert(got, gc.DeepEquals, cloud.Cloud{
   418  		Name:      "test",
   419  		Type:      "lxd",
   420  		Endpoint:  "https://192.0.0.1:8443",
   421  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   422  		Regions: []cloud.Region{{
   423  			Name:     "default",
   424  			Endpoint: "https://192.0.0.1:8443",
   425  		}},
   426  	})
   427  }
   428  
   429  func (s *providerSuite) TestFinalizeCloudNotListening(c *gc.C) {
   430  	ctrl := gomock.NewController(c)
   431  	defer ctrl.Finish()
   432  
   433  	deps := s.createProvider(ctrl)
   434  	cloudFinalizer := deps.provider.(environs.CloudFinalizer)
   435  
   436  	deps.factory.EXPECT().LocalServer().Return(nil, errors.New("bad"))
   437  
   438  	ctx := testing.NewMockFinalizeCloudContext(ctrl)
   439  	_, err := cloudFinalizer.FinalizeCloud(ctx, cloud.Cloud{
   440  		Name:      "localhost",
   441  		Type:      "lxd",
   442  		AuthTypes: []cloud.AuthType{cloud.CertificateAuthType},
   443  		Regions: []cloud.Region{{
   444  			Name: "bar",
   445  		}},
   446  	})
   447  	c.Assert(err, gc.NotNil)
   448  	c.Assert(err, gc.ErrorMatches, "bad")
   449  }
   450  
   451  func (s *providerSuite) TestDetectRegions(c *gc.C) {
   452  	ctrl := gomock.NewController(c)
   453  	defer ctrl.Finish()
   454  
   455  	deps := s.createProvider(ctrl)
   456  	cloudDetector := deps.provider.(environs.CloudRegionDetector)
   457  
   458  	regions, err := cloudDetector.DetectRegions()
   459  	c.Assert(err, jc.ErrorIsNil)
   460  	c.Assert(regions, jc.DeepEquals, []cloud.Region{{Name: lxdnames.DefaultLocalRegion}})
   461  }
   462  
   463  func (s *providerSuite) TestValidate(c *gc.C) {
   464  	ctrl := gomock.NewController(c)
   465  	defer ctrl.Finish()
   466  
   467  	deps := s.createProvider(ctrl)
   468  
   469  	validCfg, err := deps.provider.Validate(s.Config, nil)
   470  	c.Assert(err, jc.ErrorIsNil)
   471  	validAttrs := validCfg.AllAttrs()
   472  
   473  	c.Check(s.Config.AllAttrs(), gc.DeepEquals, validAttrs)
   474  }
   475  
   476  func (s *providerSuite) TestValidateWithInvalidConfig(c *gc.C) {
   477  	ctrl := gomock.NewController(c)
   478  	defer ctrl.Finish()
   479  
   480  	deps := s.createProvider(ctrl)
   481  
   482  	config, err := jujutesting.ModelConfig(c).Apply(map[string]interface{}{
   483  		"value": int64(1),
   484  	})
   485  	c.Assert(err, gc.IsNil)
   486  
   487  	_, err = deps.provider.Validate(config, nil)
   488  	c.Assert(err, gc.NotNil)
   489  }
   490  
   491  func (s *providerSuite) TestCloudSchema(c *gc.C) {
   492  	ctrl := gomock.NewController(c)
   493  	defer ctrl.Finish()
   494  
   495  	deps := s.createProvider(ctrl)
   496  
   497  	config := `
   498  auth-types: [certificate]
   499  endpoint: http://foo.com/lxd
   500  `[1:]
   501  	var v interface{}
   502  	err := yaml.Unmarshal([]byte(config), &v)
   503  	c.Assert(err, jc.ErrorIsNil)
   504  	v, err = utils.ConformYAML(v)
   505  	c.Assert(err, jc.ErrorIsNil)
   506  
   507  	err = deps.provider.CloudSchema().Validate(v)
   508  	c.Assert(err, jc.ErrorIsNil)
   509  }
   510  
   511  func (s *providerSuite) TestPingFailWithNoEndpoint(c *gc.C) {
   512  	server := httptest.NewTLSServer(http.HandlerFunc(http.NotFound))
   513  	defer server.Close()
   514  
   515  	p, err := environs.Provider("lxd")
   516  	c.Assert(err, jc.ErrorIsNil)
   517  	err = p.Ping(context.NewEmptyCloudCallContext(), server.URL)
   518  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf(
   519  		"no lxd server running at %[1]s: Failed to fetch %[1]s/1.0: 404 Not Found",
   520  		server.URL))
   521  }
   522  
   523  func (s *providerSuite) TestPingFailWithHTTP(c *gc.C) {
   524  	server := httptest.NewServer(http.HandlerFunc(http.NotFound))
   525  	defer server.Close()
   526  
   527  	p, err := environs.Provider("lxd")
   528  	c.Assert(err, jc.ErrorIsNil)
   529  	err = p.Ping(context.NewEmptyCloudCallContext(), server.URL)
   530  	httpsURL := "https://" + strings.TrimPrefix(server.URL, "http://")
   531  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf(
   532  		`no lxd server running at %[1]s: Get "%[1]s/1.0": http: server gave HTTP response to HTTPS client`,
   533  		httpsURL))
   534  }
   535  
   536  type ProviderFunctionalSuite struct {
   537  	lxd.BaseSuite
   538  
   539  	provider environs.EnvironProvider
   540  }
   541  
   542  func (s *ProviderFunctionalSuite) SetUpTest(c *gc.C) {
   543  	s.BaseSuite.SetUpTest(c)
   544  
   545  	provider, err := environs.Provider("lxd")
   546  	c.Assert(err, jc.ErrorIsNil)
   547  
   548  	s.provider = provider
   549  }
   550  
   551  func (s *ProviderFunctionalSuite) TestOpen(c *gc.C) {
   552  	env, err := environs.Open(stdcontext.TODO(), s.provider, environs.OpenParams{
   553  		Cloud:  lxdCloudSpec(),
   554  		Config: s.Config,
   555  	})
   556  	c.Assert(err, jc.ErrorIsNil)
   557  	envConfig := env.Config()
   558  
   559  	c.Check(envConfig.Name(), gc.Equals, "testmodel")
   560  }
   561  
   562  func (s *ProviderFunctionalSuite) TestPrepareConfig(c *gc.C) {
   563  	cfg, err := s.provider.PrepareConfig(environs.PrepareConfigParams{
   564  		Cloud:  lxdCloudSpec(),
   565  		Config: s.Config,
   566  	})
   567  	c.Assert(err, jc.ErrorIsNil)
   568  	c.Check(cfg, gc.NotNil)
   569  }
   570  
   571  func (s *ProviderFunctionalSuite) TestPrepareConfigUnsupportedEndpointScheme(c *gc.C) {
   572  	cloudSpec := lxdCloudSpec()
   573  	cloudSpec.Endpoint = "unix://foo"
   574  	_, err := s.provider.PrepareConfig(environs.PrepareConfigParams{
   575  		Cloud:  cloudSpec,
   576  		Config: s.Config,
   577  	})
   578  	c.Assert(err, gc.ErrorMatches, `validating cloud spec: invalid URL "unix://foo": only HTTPS is supported`)
   579  }
   580  
   581  func (s *ProviderFunctionalSuite) TestPrepareConfigUnsupportedAuthType(c *gc.C) {
   582  	cred := cloud.NewCredential("foo", nil)
   583  	_, err := s.provider.PrepareConfig(environs.PrepareConfigParams{
   584  		Cloud: environscloudspec.CloudSpec{
   585  			Type:       "lxd",
   586  			Name:       "remotehost",
   587  			Credential: &cred,
   588  		},
   589  	})
   590  	c.Assert(err, gc.ErrorMatches, `validating cloud spec: "foo" auth-type not supported`)
   591  }
   592  
   593  func (s *ProviderFunctionalSuite) TestPrepareConfigInvalidCertificateAttrs(c *gc.C) {
   594  	cred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{})
   595  	_, err := s.provider.PrepareConfig(environs.PrepareConfigParams{
   596  		Cloud: environscloudspec.CloudSpec{
   597  			Type:       "lxd",
   598  			Name:       "remotehost",
   599  			Credential: &cred,
   600  		},
   601  	})
   602  	c.Assert(err, gc.ErrorMatches, `validating cloud spec: certificate credentials not valid`)
   603  }
   604  
   605  func (s *ProviderFunctionalSuite) TestPrepareConfigEmptyAuthNonLocal(c *gc.C) {
   606  	cred := cloud.NewEmptyCredential()
   607  	_, err := s.provider.PrepareConfig(environs.PrepareConfigParams{
   608  		Cloud: environscloudspec.CloudSpec{
   609  			Type:       "lxd",
   610  			Name:       "remotehost",
   611  			Endpoint:   "8.8.8.8",
   612  			Credential: &cred,
   613  		},
   614  	})
   615  	c.Assert(err, gc.ErrorMatches, `validating cloud spec: "empty" auth-type not supported`)
   616  }
   617  
   618  type mockContext struct {
   619  	gitjujutesting.Stub
   620  }
   621  
   622  func (c *mockContext) Verbosef(f string, args ...interface{}) {
   623  	c.MethodCall(c, "Verbosef", f, args)
   624  }