github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/lxd/credentials_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lxd_test
     5  
     6  import (
     7  	"encoding/base64"
     8  	"net"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/golang/mock/gomock"
    13  	"github.com/juju/cmd/cmdtesting"
    14  	"github.com/juju/errors"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/utils"
    17  	"github.com/lxc/lxd/shared/api"
    18  	gc "gopkg.in/check.v1"
    19  
    20  	"github.com/juju/juju/cloud"
    21  	containerLXD "github.com/juju/juju/container/lxd"
    22  	"github.com/juju/juju/environs"
    23  	envtesting "github.com/juju/juju/environs/testing"
    24  	"github.com/juju/juju/juju/osenv"
    25  	"github.com/juju/juju/provider/lxd"
    26  	coretesting "github.com/juju/juju/testing"
    27  )
    28  
    29  //go:generate mockgen -package lxd -destination net_mock_test.go net Addr
    30  
    31  type credentialsSuite struct {
    32  	lxd.BaseSuite
    33  }
    34  
    35  var _ = gc.Suite(&credentialsSuite{})
    36  
    37  func (s *credentialsSuite) TestCredentialSchemas(c *gc.C) {
    38  	provider := lxd.NewProvider()
    39  	envtesting.AssertProviderAuthTypes(c, provider, "certificate", "interactive")
    40  }
    41  
    42  type credentialsSuiteDeps struct {
    43  	provider       environs.EnvironProvider
    44  	creds          environs.ProviderCredentials
    45  	server         *lxd.MockServer
    46  	serverFactory  *lxd.MockServerFactory
    47  	certReadWriter *lxd.MockCertificateReadWriter
    48  	certGenerator  *lxd.MockCertificateGenerator
    49  	netLookup      *lxd.MockNetLookup
    50  	configReader   *lxd.MockLXCConfigReader
    51  }
    52  
    53  func (s *credentialsSuite) createProvider(ctrl *gomock.Controller) credentialsSuiteDeps {
    54  	server := lxd.NewMockServer(ctrl)
    55  	factory := lxd.NewMockServerFactory(ctrl)
    56  	factory.EXPECT().LocalServer().Return(server, nil).AnyTimes()
    57  
    58  	certReadWriter := lxd.NewMockCertificateReadWriter(ctrl)
    59  	certGenerator := lxd.NewMockCertificateGenerator(ctrl)
    60  	lookup := lxd.NewMockNetLookup(ctrl)
    61  	configReader := lxd.NewMockLXCConfigReader(ctrl)
    62  	creds := lxd.NewProviderCredentials(
    63  		certReadWriter,
    64  		certGenerator,
    65  		lookup,
    66  		factory,
    67  		configReader,
    68  	)
    69  	credsRegister := creds.(environs.ProviderCredentialsRegister)
    70  
    71  	provider := lxd.NewProviderWithMocks(creds, credsRegister, factory, configReader)
    72  	return credentialsSuiteDeps{
    73  		provider:       provider,
    74  		creds:          creds,
    75  		server:         server,
    76  		serverFactory:  factory,
    77  		certReadWriter: certReadWriter,
    78  		certGenerator:  certGenerator,
    79  		netLookup:      lookup,
    80  		configReader:   configReader,
    81  	}
    82  }
    83  
    84  func (s *credentialsSuite) TestDetectCredentialsFailsWithJujuCert(c *gc.C) {
    85  	ctrl := gomock.NewController(c)
    86  	defer ctrl.Finish()
    87  
    88  	deps := s.createProvider(ctrl)
    89  
    90  	path := osenv.JujuXDGDataHomePath("lxd")
    91  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, errors.NotValidf("certs"))
    92  
    93  	_, err := deps.provider.DetectCredentials()
    94  	c.Assert(errors.Cause(err), gc.ErrorMatches, "certs not valid")
    95  }
    96  
    97  func (s *credentialsSuite) TestDetectCredentialsFailsWithJujuAndLXCCert(c *gc.C) {
    98  	ctrl := gomock.NewController(c)
    99  	defer ctrl.Finish()
   100  
   101  	deps := s.createProvider(ctrl)
   102  
   103  	path := osenv.JujuXDGDataHomePath("lxd")
   104  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   105  
   106  	path = filepath.Join(utils.Home(), ".config", "lxc")
   107  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, errors.NotValidf("certs"))
   108  
   109  	_, err := deps.provider.DetectCredentials()
   110  	c.Assert(errors.Cause(err), gc.ErrorMatches, "certs not valid")
   111  }
   112  
   113  func (s *credentialsSuite) TestDetectCredentialsGeneratesCertFailsToWriteOnError(c *gc.C) {
   114  	ctrl := gomock.NewController(c)
   115  	defer ctrl.Finish()
   116  
   117  	deps := s.createProvider(ctrl)
   118  
   119  	path := osenv.JujuXDGDataHomePath("lxd")
   120  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   121  
   122  	path = filepath.Join(utils.Home(), ".config", "lxc")
   123  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   124  
   125  	deps.certGenerator.EXPECT().Generate(true).Return(nil, nil, errors.Errorf("bad"))
   126  
   127  	_, err := deps.provider.DetectCredentials()
   128  	c.Assert(errors.Cause(err), gc.ErrorMatches, "bad")
   129  }
   130  
   131  func (s *credentialsSuite) TestDetectCredentialsGeneratesCertFailsToGetCertificateOnError(c *gc.C) {
   132  	ctrl := gomock.NewController(c)
   133  	defer ctrl.Finish()
   134  
   135  	deps := s.createProvider(ctrl)
   136  
   137  	path := osenv.JujuXDGDataHomePath("lxd")
   138  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   139  	deps.certReadWriter.EXPECT().Write(path, []byte(coretesting.CACert), []byte(coretesting.CAKey)).Return(errors.Errorf("bad"))
   140  
   141  	path = filepath.Join(utils.Home(), ".config", "lxc")
   142  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   143  
   144  	deps.certGenerator.EXPECT().Generate(true).Return([]byte(coretesting.CACert), []byte(coretesting.CAKey), nil)
   145  
   146  	_, err := deps.provider.DetectCredentials()
   147  	c.Assert(errors.Cause(err), gc.ErrorMatches, "bad")
   148  }
   149  
   150  func (s *credentialsSuite) setupLocalhost(deps credentialsSuiteDeps, c *gc.C) {
   151  	path := osenv.JujuXDGDataHomePath("lxd")
   152  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   153  
   154  	path = filepath.Join(utils.Home(), ".config", "lxc")
   155  	deps.certReadWriter.EXPECT().Read(path).Return([]byte(coretesting.CACert), []byte(coretesting.CAKey), nil)
   156  }
   157  
   158  func (s *credentialsSuite) TestRemoteDetectCredentials(c *gc.C) {
   159  	ctrl := gomock.NewController(c)
   160  	defer ctrl.Finish()
   161  
   162  	deps := s.createProvider(ctrl)
   163  	s.setupLocalhost(deps, c)
   164  
   165  	deps.configReader.EXPECT().ReadConfig(".config/lxc/config.yml").Return(lxd.LXCConfig{
   166  		DefaultRemote: "localhost",
   167  		Remotes: map[string]lxd.LXCRemoteConfig{
   168  			"nuc1": {
   169  				Addr:     "https://10.0.0.1:8443",
   170  				AuthType: "certificate",
   171  				Protocol: "lxd",
   172  				Public:   false,
   173  			},
   174  		},
   175  	}, nil)
   176  	deps.configReader.EXPECT().ReadCert(".config/lxc/servercerts/nuc1.crt").Return([]byte(coretesting.ServerCert), nil)
   177  
   178  	credentials, err := deps.provider.DetectCredentials()
   179  	c.Assert(err, jc.ErrorIsNil)
   180  
   181  	nuc1Credential := cloud.NewCredential(
   182  		cloud.CertificateAuthType,
   183  		map[string]string{
   184  			"client-cert": coretesting.CACert,
   185  			"client-key":  coretesting.CAKey,
   186  			"server-cert": coretesting.ServerCert,
   187  		},
   188  	)
   189  	nuc1Credential.Label = `LXD credential "nuc1"`
   190  
   191  	c.Assert(credentials, jc.DeepEquals, &cloud.CloudCredential{
   192  		AuthCredentials: map[string]cloud.Credential{
   193  			"nuc1": nuc1Credential,
   194  		},
   195  	})
   196  }
   197  
   198  func (s *credentialsSuite) TestRemoteDetectCredentialsWithConfigFailure(c *gc.C) {
   199  	ctrl := gomock.NewController(c)
   200  	defer ctrl.Finish()
   201  
   202  	deps := s.createProvider(ctrl)
   203  
   204  	s.setupLocalhost(deps, c)
   205  
   206  	deps.configReader.EXPECT().ReadConfig(".config/lxc/config.yml").Return(lxd.LXCConfig{}, errors.New("bad"))
   207  
   208  	credentials, err := deps.provider.DetectCredentials()
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	c.Assert(credentials, jc.DeepEquals, &cloud.CloudCredential{
   211  		AuthCredentials: map[string]cloud.Credential{},
   212  	})
   213  }
   214  
   215  func (s *credentialsSuite) TestRemoteDetectCredentialsWithCertFailure(c *gc.C) {
   216  	ctrl := gomock.NewController(c)
   217  	defer ctrl.Finish()
   218  
   219  	deps := s.createProvider(ctrl)
   220  	s.setupLocalhost(deps, c)
   221  
   222  	deps.configReader.EXPECT().ReadConfig(".config/lxc/config.yml").Return(lxd.LXCConfig{
   223  		DefaultRemote: "localhost",
   224  		Remotes: map[string]lxd.LXCRemoteConfig{
   225  			"nuc1": {
   226  				Addr:     "https://10.0.0.1:8443",
   227  				AuthType: "certificate",
   228  				Protocol: "lxd",
   229  				Public:   false,
   230  			},
   231  		},
   232  	}, nil)
   233  	deps.configReader.EXPECT().ReadCert(".config/lxc/servercerts/nuc1.crt").Return(nil, errors.New("bad"))
   234  
   235  	credentials, err := deps.provider.DetectCredentials()
   236  	c.Assert(err, jc.ErrorIsNil)
   237  	c.Assert(credentials, jc.DeepEquals, &cloud.CloudCredential{
   238  		AuthCredentials: map[string]cloud.Credential{},
   239  	})
   240  }
   241  
   242  func (s *credentialsSuite) TestRegisterCredentials(c *gc.C) {
   243  	ctrl := gomock.NewController(c)
   244  	defer ctrl.Finish()
   245  
   246  	deps := s.createProvider(ctrl)
   247  
   248  	deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", nil)
   249  	deps.server.EXPECT().ServerCertificate().Return("server-cert")
   250  
   251  	path := osenv.JujuXDGDataHomePath("lxd")
   252  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   253  	deps.certReadWriter.EXPECT().Write(path, []byte(coretesting.CACert), []byte(coretesting.CAKey)).Return(nil)
   254  
   255  	path = filepath.Join(utils.Home(), ".config", "lxc")
   256  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   257  	deps.certGenerator.EXPECT().Generate(true).Return([]byte(coretesting.CACert), []byte(coretesting.CAKey), nil)
   258  
   259  	expected := cloud.NewCredential(
   260  		cloud.CertificateAuthType,
   261  		map[string]string{
   262  			"client-cert": coretesting.CACert,
   263  			"client-key":  coretesting.CAKey,
   264  			"server-cert": "server-cert",
   265  		},
   266  	)
   267  	expected.Label = `LXD credential "localhost"`
   268  
   269  	provider := deps.provider.(environs.ProviderCredentialsRegister)
   270  	credentials, err := provider.RegisterCredentials(cloud.Cloud{
   271  		Name: "localhost",
   272  	})
   273  	c.Assert(err, jc.ErrorIsNil)
   274  	c.Assert(credentials, jc.DeepEquals, map[string]*cloud.CloudCredential{
   275  		"localhost": {
   276  			DefaultCredential: "localhost",
   277  			AuthCredentials: map[string]cloud.Credential{
   278  				"localhost": expected,
   279  			},
   280  		},
   281  	})
   282  }
   283  
   284  func (s *credentialsSuite) TestRegisterCredentialsWithAlternativeCloudName(c *gc.C) {
   285  	ctrl := gomock.NewController(c)
   286  	defer ctrl.Finish()
   287  
   288  	deps := s.createProvider(ctrl)
   289  
   290  	deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", nil)
   291  	deps.server.EXPECT().ServerCertificate().Return("server-cert")
   292  
   293  	path := osenv.JujuXDGDataHomePath("lxd")
   294  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   295  	deps.certReadWriter.EXPECT().Write(path, []byte(coretesting.CACert), []byte(coretesting.CAKey)).Return(nil)
   296  
   297  	path = filepath.Join(utils.Home(), ".config", "lxc")
   298  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   299  	deps.certGenerator.EXPECT().Generate(true).Return([]byte(coretesting.CACert), []byte(coretesting.CAKey), nil)
   300  
   301  	expected := cloud.NewCredential(
   302  		cloud.CertificateAuthType,
   303  		map[string]string{
   304  			"client-cert": coretesting.CACert,
   305  			"client-key":  coretesting.CAKey,
   306  			"server-cert": "server-cert",
   307  		},
   308  	)
   309  	expected.Label = `LXD credential "localhost"`
   310  
   311  	provider := deps.provider.(environs.ProviderCredentialsRegister)
   312  	credentials, err := provider.RegisterCredentials(cloud.Cloud{
   313  		Name: "lxd",
   314  	})
   315  	c.Assert(err, jc.ErrorIsNil)
   316  	c.Assert(credentials, jc.DeepEquals, map[string]*cloud.CloudCredential{
   317  		"lxd": {
   318  			DefaultCredential: "lxd",
   319  			AuthCredentials: map[string]cloud.Credential{
   320  				"lxd": expected,
   321  			},
   322  		},
   323  	})
   324  }
   325  
   326  func (s *credentialsSuite) TestRegisterCredentialsUsesJujuCert(c *gc.C) {
   327  	ctrl := gomock.NewController(c)
   328  	defer ctrl.Finish()
   329  
   330  	deps := s.createProvider(ctrl)
   331  
   332  	deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", nil)
   333  	deps.server.EXPECT().ServerCertificate().Return("server-cert")
   334  
   335  	path := osenv.JujuXDGDataHomePath("lxd")
   336  	deps.certReadWriter.EXPECT().Read(path).Return([]byte(coretesting.CACert), []byte(coretesting.CAKey), nil)
   337  
   338  	provider := deps.provider.(environs.ProviderCredentialsRegister)
   339  	credentials, err := provider.RegisterCredentials(cloud.Cloud{
   340  		Name: "localhost",
   341  	})
   342  	c.Assert(err, jc.ErrorIsNil)
   343  
   344  	expected := cloud.NewCredential(
   345  		cloud.CertificateAuthType,
   346  		map[string]string{
   347  			"client-cert": coretesting.CACert,
   348  			"client-key":  coretesting.CAKey,
   349  			"server-cert": "server-cert",
   350  		},
   351  	)
   352  	expected.Label = `LXD credential "localhost"`
   353  
   354  	c.Assert(credentials, jc.DeepEquals, map[string]*cloud.CloudCredential{
   355  		"localhost": {
   356  			DefaultCredential: "localhost",
   357  			AuthCredentials: map[string]cloud.Credential{
   358  				"localhost": expected,
   359  			},
   360  		},
   361  	})
   362  }
   363  
   364  func (s *credentialsSuite) TestRegisterCredentialsUsesLXCCert(c *gc.C) {
   365  	ctrl := gomock.NewController(c)
   366  	defer ctrl.Finish()
   367  
   368  	deps := s.createProvider(ctrl)
   369  
   370  	deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", nil)
   371  	deps.server.EXPECT().ServerCertificate().Return("server-cert")
   372  
   373  	path := osenv.JujuXDGDataHomePath("lxd")
   374  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   375  
   376  	path = filepath.Join(utils.Home(), ".config", "lxc")
   377  	deps.certReadWriter.EXPECT().Read(path).Return([]byte(coretesting.CACert), []byte(coretesting.CAKey), nil)
   378  
   379  	provider := deps.provider.(environs.ProviderCredentialsRegister)
   380  	credentials, err := provider.RegisterCredentials(cloud.Cloud{
   381  		Name: "localhost",
   382  	})
   383  	c.Assert(err, jc.ErrorIsNil)
   384  
   385  	expected := cloud.NewCredential(
   386  		cloud.CertificateAuthType,
   387  		map[string]string{
   388  			"client-cert": coretesting.CACert,
   389  			"client-key":  coretesting.CAKey,
   390  			"server-cert": "server-cert",
   391  		},
   392  	)
   393  	expected.Label = `LXD credential "localhost"`
   394  
   395  	c.Assert(credentials, jc.DeepEquals, map[string]*cloud.CloudCredential{
   396  		"localhost": {
   397  			DefaultCredential: "localhost",
   398  			AuthCredentials: map[string]cloud.Credential{
   399  				"localhost": expected,
   400  			},
   401  		},
   402  	})
   403  }
   404  
   405  func (s *credentialsSuite) TestFinalizeCredentialLocal(c *gc.C) {
   406  	ctrl := gomock.NewController(c)
   407  	defer ctrl.Finish()
   408  
   409  	deps := s.createProvider(ctrl)
   410  
   411  	deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", nil)
   412  	deps.server.EXPECT().ServerCertificate().Return("server-cert")
   413  
   414  	localhostIP := net.IPv4(127, 0, 0, 1)
   415  	ipNet := &net.IPNet{IP: localhostIP, Mask: localhostIP.DefaultMask()}
   416  
   417  	deps.netLookup.EXPECT().LookupHost("localhost").Return([]string{"127.0.0.1"}, nil)
   418  	deps.netLookup.EXPECT().InterfaceAddrs().Return([]net.Addr{ipNet}, nil)
   419  
   420  	out, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), environs.FinalizeCredentialParams{
   421  		CloudEndpoint: "localhost",
   422  		Credential: cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   423  			"client-cert": coretesting.CACert,
   424  			"client-key":  coretesting.CAKey,
   425  		}),
   426  	})
   427  	c.Assert(err, jc.ErrorIsNil)
   428  
   429  	c.Assert(out.AuthType(), gc.Equals, cloud.CertificateAuthType)
   430  	c.Assert(out.Attributes(), jc.DeepEquals, map[string]string{
   431  		"client-cert": coretesting.CACert,
   432  		"client-key":  coretesting.CAKey,
   433  		"server-cert": "server-cert",
   434  	})
   435  }
   436  
   437  func (s *credentialsSuite) TestFinalizeCredentialLocalAddCert(c *gc.C) {
   438  	ctrl := gomock.NewController(c)
   439  	defer ctrl.Finish()
   440  
   441  	deps := s.createProvider(ctrl)
   442  
   443  	deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", nil)
   444  	deps.server.EXPECT().ServerCertificate().Return("server-cert")
   445  
   446  	out, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), environs.FinalizeCredentialParams{
   447  		CloudEndpoint: "",
   448  		Credential: cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   449  			"client-cert": coretesting.CACert,
   450  			"client-key":  coretesting.CAKey,
   451  		}),
   452  	})
   453  	c.Assert(err, jc.ErrorIsNil)
   454  
   455  	c.Assert(out.AuthType(), gc.Equals, cloud.CertificateAuthType)
   456  	c.Assert(out.Attributes(), jc.DeepEquals, map[string]string{
   457  		"client-cert": coretesting.CACert,
   458  		"client-key":  coretesting.CAKey,
   459  		"server-cert": "server-cert",
   460  	})
   461  }
   462  
   463  func (s *credentialsSuite) TestFinalizeCredentialLocalAddCertAlreadyExists(c *gc.C) {
   464  	// If we get back an error from CreateClientCertificate, we'll make another
   465  	// call to GetCertificate. If that call succeeds, then we assume
   466  	// that the CreateClientCertificate failure was due to a concurrent call.
   467  
   468  	ctrl := gomock.NewController(c)
   469  	defer ctrl.Finish()
   470  
   471  	deps := s.createProvider(ctrl)
   472  
   473  	gomock.InOrder(
   474  		deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", errors.New("not found")),
   475  		deps.server.EXPECT().CreateClientCertificate(s.clientCert()).Return(errors.New("UNIQUE constraint failed: interactives.fingerprint")),
   476  		deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", nil),
   477  		deps.server.EXPECT().ServerCertificate().Return("server-cert"),
   478  	)
   479  
   480  	out, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), environs.FinalizeCredentialParams{
   481  		CloudEndpoint: "",
   482  		Credential: cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   483  			"client-cert": coretesting.CACert,
   484  			"client-key":  coretesting.CAKey,
   485  		}),
   486  	})
   487  	c.Assert(err, jc.ErrorIsNil)
   488  
   489  	c.Assert(out.AuthType(), gc.Equals, cloud.CertificateAuthType)
   490  	c.Assert(out.Attributes(), jc.DeepEquals, map[string]string{
   491  		"client-cert": coretesting.CACert,
   492  		"client-key":  coretesting.CAKey,
   493  		"server-cert": "server-cert",
   494  	})
   495  }
   496  
   497  func (s *credentialsSuite) TestFinalizeCredentialLocalAddCertFatal(c *gc.C) {
   498  	// If we get back an error from CreateClientCertificate, we'll make another
   499  	// call to GetCertificate. If that call succeeds, then we assume
   500  	// that the CreateClientCertificate failure was due to a concurrent call.
   501  
   502  	ctrl := gomock.NewController(c)
   503  	defer ctrl.Finish()
   504  
   505  	deps := s.createProvider(ctrl)
   506  
   507  	gomock.InOrder(
   508  		deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", errors.New("not found")),
   509  		deps.server.EXPECT().CreateClientCertificate(s.clientCert()).Return(errors.New("UNIQUE constraint failed: interactives.fingerprint")),
   510  		deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", errors.New("not found")),
   511  	)
   512  
   513  	_, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), environs.FinalizeCredentialParams{
   514  		CloudEndpoint: "",
   515  		Credential: cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   516  			"client-cert": coretesting.CACert,
   517  			"client-key":  coretesting.CAKey,
   518  		}),
   519  	})
   520  	c.Assert(err, gc.ErrorMatches, "adding certificate \"juju\": UNIQUE constraint failed: interactives.fingerprint")
   521  }
   522  
   523  func (s *credentialsSuite) TestFinalizeCredentialLocalCertificateWithEmptyClientCert(c *gc.C) {
   524  	ctrl := gomock.NewController(c)
   525  	defer ctrl.Finish()
   526  
   527  	deps := s.createProvider(ctrl)
   528  
   529  	ctx := cmdtesting.Context(c)
   530  	_, err := deps.provider.FinalizeCredential(ctx, environs.FinalizeCredentialParams{
   531  		CloudEndpoint: "localhost",
   532  		Credential:    cloud.NewCredential("certificate", map[string]string{}),
   533  	})
   534  	c.Assert(err, gc.ErrorMatches, `missing or empty "client-cert" attribute not valid`)
   535  }
   536  
   537  func (s *credentialsSuite) TestFinalizeCredentialLocalCertificateWithEmptyClientKey(c *gc.C) {
   538  	ctrl := gomock.NewController(c)
   539  	defer ctrl.Finish()
   540  
   541  	deps := s.createProvider(ctrl)
   542  
   543  	ctx := cmdtesting.Context(c)
   544  	_, err := deps.provider.FinalizeCredential(ctx, environs.FinalizeCredentialParams{
   545  		CloudEndpoint: "localhost",
   546  		Credential: cloud.NewCredential("certificate", map[string]string{
   547  			"client-cert": coretesting.CACert,
   548  		}),
   549  	})
   550  	c.Assert(err, gc.ErrorMatches, `missing or empty "client-key" attribute not valid`)
   551  }
   552  
   553  func (s *credentialsSuite) TestFinalizeCredentialLocalCertificate(c *gc.C) {
   554  	ctrl := gomock.NewController(c)
   555  	defer ctrl.Finish()
   556  
   557  	deps := s.createProvider(ctrl)
   558  
   559  	ctx := cmdtesting.Context(c)
   560  	out, err := deps.provider.FinalizeCredential(ctx, environs.FinalizeCredentialParams{
   561  		CloudEndpoint: "localhost",
   562  		Credential: cloud.NewCredential("certificate", map[string]string{
   563  			"client-cert": "/path/to/client/cert.crt",
   564  			"client-key":  "/path/to/client/key.key",
   565  			"server-cert": "server-cert",
   566  		}),
   567  	})
   568  	c.Assert(err, jc.ErrorIsNil)
   569  
   570  	c.Assert(out.AuthType(), gc.Equals, cloud.AuthType("certificate"))
   571  	c.Assert(out.Attributes(), jc.DeepEquals, map[string]string{
   572  		"client-cert": "/path/to/client/cert.crt",
   573  		"client-key":  "/path/to/client/key.key",
   574  		"server-cert": "server-cert",
   575  	})
   576  }
   577  
   578  func (s *credentialsSuite) TestFinalizeCredentialNonLocalCertificate(c *gc.C) {
   579  	ctrl := gomock.NewController(c)
   580  	defer ctrl.Finish()
   581  
   582  	deps := s.createProvider(ctrl)
   583  
   584  	// Patch the interface addresses for the calling machine, so
   585  	// it appears that we're not on the LXD server host.
   586  	_, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), environs.FinalizeCredentialParams{
   587  		CloudEndpoint: "8.8.8.8",
   588  		Credential:    cloud.NewCredential("certificate", map[string]string{}),
   589  	})
   590  	c.Assert(err, gc.ErrorMatches, `missing or empty "client-cert" attribute not valid`)
   591  }
   592  
   593  func (s *credentialsSuite) TestFinalizeCredentialNonLocal(c *gc.C) {
   594  	ctrl := gomock.NewController(c)
   595  	defer ctrl.Finish()
   596  
   597  	deps := s.createProvider(ctrl)
   598  
   599  	insecureCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   600  		"client-cert":    coretesting.CACert,
   601  		"client-key":     coretesting.CAKey,
   602  		"trust-password": "fred",
   603  	})
   604  	insecureSpec := environs.CloudSpec{
   605  		Endpoint:   "8.8.8.8",
   606  		Credential: &insecureCred,
   607  	}
   608  	secureCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   609  		"client-cert": coretesting.CACert,
   610  		"client-key":  coretesting.CAKey,
   611  		"server-cert": coretesting.ServerCert,
   612  	})
   613  	secureSpec := environs.CloudSpec{
   614  		Endpoint:   "8.8.8.8",
   615  		Credential: &secureCred,
   616  	}
   617  	params := environs.FinalizeCredentialParams{
   618  		CloudEndpoint: "8.8.8.8",
   619  		Credential:    insecureCred,
   620  	}
   621  	clientCert := containerLXD.NewCertificate([]byte(coretesting.CACert), []byte(coretesting.CAKey))
   622  	clientX509Cert, err := clientCert.X509()
   623  	c.Assert(err, jc.ErrorIsNil)
   624  	clientX509Base64 := base64.StdEncoding.EncodeToString(clientX509Cert.Raw)
   625  	fingerprint, err := clientCert.Fingerprint()
   626  	c.Assert(err, jc.ErrorIsNil)
   627  
   628  	deps.netLookup.EXPECT().LookupHost("8.8.8.8").Return([]string{}, nil)
   629  	deps.netLookup.EXPECT().InterfaceAddrs().Return([]net.Addr{}, nil)
   630  	deps.serverFactory.EXPECT().InsecureRemoteServer(insecureSpec).Return(deps.server, nil)
   631  	deps.server.EXPECT().GetCertificate(fingerprint).Return(nil, "", errors.New("not found"))
   632  	deps.server.EXPECT().CreateCertificate(api.CertificatesPost{
   633  		CertificatePut: api.CertificatePut{
   634  			Name: insecureCred.Label,
   635  			Type: "client",
   636  		},
   637  		Certificate: clientX509Base64,
   638  		Password:    "fred",
   639  	}).Return(nil)
   640  	deps.server.EXPECT().GetServer().Return(&api.Server{
   641  		Environment: api.ServerEnvironment{
   642  			Certificate: coretesting.ServerCert,
   643  		},
   644  	}, "etag", nil)
   645  	deps.serverFactory.EXPECT().RemoteServer(secureSpec).Return(deps.server, nil)
   646  	deps.server.EXPECT().ServerCertificate().Return(coretesting.ServerCert)
   647  
   648  	expected := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   649  		"client-cert": coretesting.CACert,
   650  		"client-key":  coretesting.CAKey,
   651  		"server-cert": coretesting.ServerCert,
   652  	})
   653  
   654  	got, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), params)
   655  	c.Assert(err, jc.ErrorIsNil)
   656  	c.Assert(got, jc.DeepEquals, &expected)
   657  }
   658  
   659  func (s *credentialsSuite) TestFinalizeCredentialNonLocalWithCertAlreadyExists(c *gc.C) {
   660  	ctrl := gomock.NewController(c)
   661  	defer ctrl.Finish()
   662  
   663  	deps := s.createProvider(ctrl)
   664  
   665  	insecureCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   666  		"client-cert":    coretesting.CACert,
   667  		"client-key":     coretesting.CAKey,
   668  		"trust-password": "fred",
   669  	})
   670  	insecureSpec := environs.CloudSpec{
   671  		Endpoint:   "8.8.8.8",
   672  		Credential: &insecureCred,
   673  	}
   674  	secureCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   675  		"client-cert": coretesting.CACert,
   676  		"client-key":  coretesting.CAKey,
   677  		"server-cert": coretesting.ServerCert,
   678  	})
   679  	secureSpec := environs.CloudSpec{
   680  		Endpoint:   "8.8.8.8",
   681  		Credential: &secureCred,
   682  	}
   683  	params := environs.FinalizeCredentialParams{
   684  		CloudEndpoint: "8.8.8.8",
   685  		Credential:    insecureCred,
   686  	}
   687  	clientCert := containerLXD.NewCertificate([]byte(coretesting.CACert), []byte(coretesting.CAKey))
   688  	fingerprint, err := clientCert.Fingerprint()
   689  	c.Assert(err, jc.ErrorIsNil)
   690  
   691  	deps.netLookup.EXPECT().LookupHost("8.8.8.8").Return([]string{}, nil)
   692  	deps.netLookup.EXPECT().InterfaceAddrs().Return([]net.Addr{}, nil)
   693  	deps.serverFactory.EXPECT().InsecureRemoteServer(insecureSpec).Return(deps.server, nil)
   694  	deps.server.EXPECT().GetCertificate(fingerprint).Return(&api.Certificate{}, "", nil)
   695  	deps.server.EXPECT().GetServer().Return(&api.Server{
   696  		Environment: api.ServerEnvironment{
   697  			Certificate: coretesting.ServerCert,
   698  		},
   699  	}, "etag", nil)
   700  	deps.serverFactory.EXPECT().RemoteServer(secureSpec).Return(deps.server, nil)
   701  	deps.server.EXPECT().ServerCertificate().Return(coretesting.ServerCert)
   702  
   703  	expected := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   704  		"client-cert": coretesting.CACert,
   705  		"client-key":  coretesting.CAKey,
   706  		"server-cert": coretesting.ServerCert,
   707  	})
   708  
   709  	got, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), params)
   710  	c.Assert(err, jc.ErrorIsNil)
   711  	c.Assert(got, jc.DeepEquals, &expected)
   712  }
   713  
   714  func (s *credentialsSuite) TestFinalizeCredentialRemoteWithInsecureError(c *gc.C) {
   715  	ctrl := gomock.NewController(c)
   716  	defer ctrl.Finish()
   717  
   718  	deps := s.createProvider(ctrl)
   719  
   720  	insecureCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   721  		"client-cert":    coretesting.CACert,
   722  		"client-key":     coretesting.CAKey,
   723  		"trust-password": "fred",
   724  	})
   725  	insecureSpec := environs.CloudSpec{
   726  		Endpoint:   "8.8.8.8",
   727  		Credential: &insecureCred,
   728  	}
   729  	params := environs.FinalizeCredentialParams{
   730  		CloudEndpoint: "8.8.8.8",
   731  		Credential:    insecureCred,
   732  	}
   733  
   734  	deps.netLookup.EXPECT().LookupHost("8.8.8.8").Return([]string{}, nil)
   735  	deps.netLookup.EXPECT().InterfaceAddrs().Return([]net.Addr{}, nil)
   736  	deps.serverFactory.EXPECT().InsecureRemoteServer(insecureSpec).Return(nil, errors.New("bad"))
   737  
   738  	_, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), params)
   739  	c.Assert(errors.Cause(err).Error(), gc.Equals, "bad")
   740  }
   741  
   742  func (s *credentialsSuite) TestFinalizeCredentialRemoteWithCreateCertificateError(c *gc.C) {
   743  	ctrl := gomock.NewController(c)
   744  	defer ctrl.Finish()
   745  
   746  	deps := s.createProvider(ctrl)
   747  
   748  	insecureCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   749  		"client-cert":    coretesting.CACert,
   750  		"client-key":     coretesting.CAKey,
   751  		"trust-password": "fred",
   752  	})
   753  	insecureSpec := environs.CloudSpec{
   754  		Endpoint:   "8.8.8.8",
   755  		Credential: &insecureCred,
   756  	}
   757  	params := environs.FinalizeCredentialParams{
   758  		CloudEndpoint: "8.8.8.8",
   759  		Credential:    insecureCred,
   760  	}
   761  	clientCert := containerLXD.NewCertificate([]byte(coretesting.CACert), []byte(coretesting.CAKey))
   762  	clientX509Cert, err := clientCert.X509()
   763  	c.Assert(err, jc.ErrorIsNil)
   764  	clientX509Base64 := base64.StdEncoding.EncodeToString(clientX509Cert.Raw)
   765  	fingerprint, err := clientCert.Fingerprint()
   766  	c.Assert(err, jc.ErrorIsNil)
   767  
   768  	deps.netLookup.EXPECT().LookupHost("8.8.8.8").Return([]string{}, nil)
   769  	deps.netLookup.EXPECT().InterfaceAddrs().Return([]net.Addr{}, nil)
   770  	deps.serverFactory.EXPECT().InsecureRemoteServer(insecureSpec).Return(deps.server, nil)
   771  	deps.server.EXPECT().GetCertificate(fingerprint).Return(nil, "", errors.New("not found"))
   772  	deps.server.EXPECT().CreateCertificate(api.CertificatesPost{
   773  		CertificatePut: api.CertificatePut{
   774  			Name: insecureCred.Label,
   775  			Type: "client",
   776  		},
   777  		Certificate: clientX509Base64,
   778  		Password:    "fred",
   779  	}).Return(errors.New("bad"))
   780  
   781  	_, err = deps.provider.FinalizeCredential(cmdtesting.Context(c), params)
   782  	c.Assert(errors.Cause(err).Error(), gc.Equals, "bad")
   783  }
   784  
   785  func (s *credentialsSuite) TestFinalizeCredentialRemoveWithGetServerError(c *gc.C) {
   786  	ctrl := gomock.NewController(c)
   787  	defer ctrl.Finish()
   788  
   789  	deps := s.createProvider(ctrl)
   790  
   791  	insecureCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   792  		"client-cert":    coretesting.CACert,
   793  		"client-key":     coretesting.CAKey,
   794  		"trust-password": "fred",
   795  	})
   796  	insecureSpec := environs.CloudSpec{
   797  		Endpoint:   "8.8.8.8",
   798  		Credential: &insecureCred,
   799  	}
   800  	params := environs.FinalizeCredentialParams{
   801  		CloudEndpoint: "8.8.8.8",
   802  		Credential:    insecureCred,
   803  	}
   804  	clientCert := containerLXD.NewCertificate([]byte(coretesting.CACert), []byte(coretesting.CAKey))
   805  	clientX509Cert, err := clientCert.X509()
   806  	c.Assert(err, jc.ErrorIsNil)
   807  	clientX509Base64 := base64.StdEncoding.EncodeToString(clientX509Cert.Raw)
   808  	fingerprint, err := clientCert.Fingerprint()
   809  	c.Assert(err, jc.ErrorIsNil)
   810  
   811  	deps.netLookup.EXPECT().LookupHost("8.8.8.8").Return([]string{}, nil)
   812  	deps.netLookup.EXPECT().InterfaceAddrs().Return([]net.Addr{}, nil)
   813  	deps.serverFactory.EXPECT().InsecureRemoteServer(insecureSpec).Return(deps.server, nil)
   814  	deps.server.EXPECT().GetCertificate(fingerprint).Return(nil, "", errors.New("not found"))
   815  	deps.server.EXPECT().CreateCertificate(api.CertificatesPost{
   816  		CertificatePut: api.CertificatePut{
   817  			Name: insecureCred.Label,
   818  			Type: "client",
   819  		},
   820  		Certificate: clientX509Base64,
   821  		Password:    "fred",
   822  	}).Return(nil)
   823  	deps.server.EXPECT().GetServer().Return(nil, "etag", errors.New("bad"))
   824  
   825  	_, err = deps.provider.FinalizeCredential(cmdtesting.Context(c), params)
   826  	c.Assert(errors.Cause(err).Error(), gc.Equals, "bad")
   827  }
   828  
   829  func (s *credentialsSuite) TestFinalizeCredentialRemoteWithNewRemoteServerError(c *gc.C) {
   830  	ctrl := gomock.NewController(c)
   831  	defer ctrl.Finish()
   832  
   833  	deps := s.createProvider(ctrl)
   834  
   835  	insecureCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   836  		"client-cert":    coretesting.CACert,
   837  		"client-key":     coretesting.CAKey,
   838  		"trust-password": "fred",
   839  	})
   840  	insecureSpec := environs.CloudSpec{
   841  		Endpoint:   "8.8.8.8",
   842  		Credential: &insecureCred,
   843  	}
   844  	secureCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   845  		"client-cert": coretesting.CACert,
   846  		"client-key":  coretesting.CAKey,
   847  		"server-cert": coretesting.ServerCert,
   848  	})
   849  	secureSpec := environs.CloudSpec{
   850  		Endpoint:   "8.8.8.8",
   851  		Credential: &secureCred,
   852  	}
   853  	params := environs.FinalizeCredentialParams{
   854  		CloudEndpoint: "8.8.8.8",
   855  		Credential:    insecureCred,
   856  	}
   857  	clientCert := containerLXD.NewCertificate([]byte(coretesting.CACert), []byte(coretesting.CAKey))
   858  	clientX509Cert, err := clientCert.X509()
   859  	c.Assert(err, jc.ErrorIsNil)
   860  	clientX509Base64 := base64.StdEncoding.EncodeToString(clientX509Cert.Raw)
   861  	fingerprint, err := clientCert.Fingerprint()
   862  	c.Assert(err, jc.ErrorIsNil)
   863  
   864  	deps.netLookup.EXPECT().LookupHost("8.8.8.8").Return([]string{}, nil)
   865  	deps.netLookup.EXPECT().InterfaceAddrs().Return([]net.Addr{}, nil)
   866  	deps.serverFactory.EXPECT().InsecureRemoteServer(insecureSpec).Return(deps.server, nil)
   867  	deps.server.EXPECT().GetCertificate(fingerprint).Return(nil, "", errors.New("not found"))
   868  	deps.server.EXPECT().CreateCertificate(api.CertificatesPost{
   869  		CertificatePut: api.CertificatePut{
   870  			Name: insecureCred.Label,
   871  			Type: "client",
   872  		},
   873  		Certificate: clientX509Base64,
   874  		Password:    "fred",
   875  	}).Return(nil)
   876  	deps.server.EXPECT().GetServer().Return(&api.Server{
   877  		Environment: api.ServerEnvironment{
   878  			Certificate: coretesting.ServerCert,
   879  		},
   880  	}, "etag", nil)
   881  	deps.serverFactory.EXPECT().RemoteServer(secureSpec).Return(nil, errors.New("bad"))
   882  
   883  	_, err = deps.provider.FinalizeCredential(cmdtesting.Context(c), params)
   884  	c.Assert(errors.Cause(err).Error(), gc.Equals, "bad")
   885  }
   886  
   887  func (s *credentialsSuite) TestInteractiveFinalizeCredentialWithValidCredentials(c *gc.C) {
   888  	ctrl := gomock.NewController(c)
   889  	defer ctrl.Finish()
   890  
   891  	deps := s.createProvider(ctrl)
   892  
   893  	out, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), environs.FinalizeCredentialParams{
   894  		CloudEndpoint: "localhost",
   895  		Credential: cloud.NewCredential("interactive", map[string]string{
   896  			"client-cert": coretesting.CACert,
   897  			"client-key":  coretesting.CAKey,
   898  			"server-cert": "server-cert",
   899  		}),
   900  	})
   901  	c.Assert(err, jc.ErrorIsNil)
   902  
   903  	c.Assert(out.AuthType(), gc.Equals, cloud.AuthType("interactive"))
   904  	c.Assert(out.Attributes(), jc.DeepEquals, map[string]string{
   905  		"client-cert": coretesting.CACert,
   906  		"client-key":  coretesting.CAKey,
   907  		"server-cert": "server-cert",
   908  	})
   909  }
   910  
   911  func (s *credentialsSuite) TestInteractiveFinalizeCredentialWithTrustPassword(c *gc.C) {
   912  	ctrl := gomock.NewController(c)
   913  	defer ctrl.Finish()
   914  
   915  	deps := s.createProvider(ctrl)
   916  
   917  	path := osenv.JujuXDGDataHomePath("lxd")
   918  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, os.ErrNotExist)
   919  
   920  	path = filepath.Join(utils.Home(), ".config", "lxc")
   921  	deps.certReadWriter.EXPECT().Read(path).Return([]byte(coretesting.CACert), []byte(coretesting.CAKey), nil)
   922  
   923  	deps.server.EXPECT().GetCertificate(s.clientCertFingerprint(c)).Return(nil, "", nil)
   924  	deps.server.EXPECT().ServerCertificate().Return("server-cert")
   925  
   926  	localhostIP := net.IPv4(127, 0, 0, 1)
   927  	ipNet := &net.IPNet{IP: localhostIP, Mask: localhostIP.DefaultMask()}
   928  
   929  	deps.netLookup.EXPECT().LookupHost("localhost").Return([]string{"127.0.0.1"}, nil)
   930  	deps.netLookup.EXPECT().InterfaceAddrs().Return([]net.Addr{ipNet}, nil)
   931  
   932  	out, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), environs.FinalizeCredentialParams{
   933  		CloudEndpoint: "localhost",
   934  		Credential: cloud.NewCredential("interactive", map[string]string{
   935  			"trust-password": "password1",
   936  		}),
   937  	})
   938  	c.Assert(err, jc.ErrorIsNil)
   939  
   940  	c.Assert(out.AuthType(), gc.Equals, cloud.CertificateAuthType)
   941  	c.Assert(out.Attributes(), jc.DeepEquals, map[string]string{
   942  		"client-cert": coretesting.CACert,
   943  		"client-key":  coretesting.CAKey,
   944  		"server-cert": "server-cert",
   945  	})
   946  }
   947  
   948  func (s *credentialsSuite) TestInteractiveFinalizeCredentialWithCertFailure(c *gc.C) {
   949  	ctrl := gomock.NewController(c)
   950  	defer ctrl.Finish()
   951  
   952  	deps := s.createProvider(ctrl)
   953  
   954  	path := osenv.JujuXDGDataHomePath("lxd")
   955  	deps.certReadWriter.EXPECT().Read(path).Return(nil, nil, errors.New("bad"))
   956  
   957  	_, err := deps.provider.FinalizeCredential(cmdtesting.Context(c), environs.FinalizeCredentialParams{
   958  		CloudEndpoint: "localhost",
   959  		Credential: cloud.NewCredential("interactive", map[string]string{
   960  			"trust-password": "password1",
   961  		}),
   962  	})
   963  	c.Assert(errors.Cause(err).Error(), gc.Equals, "bad")
   964  }
   965  
   966  func (s *credentialsSuite) clientCert() *containerLXD.Certificate {
   967  	return &containerLXD.Certificate{
   968  		Name:    "juju",
   969  		CertPEM: []byte(coretesting.CACert),
   970  		KeyPEM:  []byte(coretesting.CAKey),
   971  	}
   972  }
   973  
   974  func (s *credentialsSuite) clientCertFingerprint(c *gc.C) string {
   975  	fp, err := s.clientCert().Fingerprint()
   976  	c.Assert(err, jc.ErrorIsNil)
   977  	return fp
   978  }
   979  
   980  func (s *credentialsSuite) TestGetCertificates(c *gc.C) {
   981  	cred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   982  		"client-cert": coretesting.CACert,
   983  		"client-key":  coretesting.CAKey,
   984  		"server-cert": "server.crt",
   985  	})
   986  	cert, server, ok := lxd.GetCertificates(cred)
   987  	c.Assert(ok, gc.Equals, true)
   988  	c.Assert(cert, jc.DeepEquals, s.clientCert())
   989  	c.Assert(server, gc.Equals, "server.crt")
   990  }
   991  
   992  func (s *credentialsSuite) TestGetCertificatesMissingClientCert(c *gc.C) {
   993  	cred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   994  		"client-key":  coretesting.CAKey,
   995  		"server-cert": "server.crt",
   996  	})
   997  	_, _, ok := lxd.GetCertificates(cred)
   998  	c.Assert(ok, gc.Equals, false)
   999  }
  1000  
  1001  func (s *credentialsSuite) TestGetCertificatesMissingClientKey(c *gc.C) {
  1002  	cred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
  1003  		"client-cert": coretesting.CACert,
  1004  		"server-cert": "server.crt",
  1005  	})
  1006  	_, _, ok := lxd.GetCertificates(cred)
  1007  	c.Assert(ok, gc.Equals, false)
  1008  }
  1009  
  1010  func (s *credentialsSuite) TestGetCertificatesMissingServerCert(c *gc.C) {
  1011  	cred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
  1012  		"client-cert": coretesting.CACert,
  1013  		"client-key":  coretesting.CAKey,
  1014  	})
  1015  	_, _, ok := lxd.GetCertificates(cred)
  1016  	c.Assert(ok, gc.Equals, false)
  1017  }