github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/environs/bootstrap/config_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package bootstrap_test
     5  
     6  import (
     7  	"os"
     8  	"time"
     9  
    10  	gitjujutesting "github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/environs/bootstrap"
    15  	"github.com/juju/juju/juju/osenv"
    16  	"github.com/juju/juju/testing"
    17  )
    18  
    19  type ConfigSuite struct {
    20  	testing.FakeJujuXDGDataHomeSuite
    21  }
    22  
    23  var _ = gc.Suite(&ConfigSuite{})
    24  
    25  func (*ConfigSuite) TestDefaultConfig(c *gc.C) {
    26  	cfg, err := bootstrap.NewConfig(nil)
    27  	c.Assert(err, jc.ErrorIsNil)
    28  
    29  	// These three things are generated.
    30  	c.Assert(cfg.AdminSecret, gc.Not(gc.HasLen), 0)
    31  	c.Assert(cfg.CACert, gc.Not(gc.HasLen), 0)
    32  	c.Assert(cfg.CAPrivateKey, gc.Not(gc.HasLen), 0)
    33  
    34  	c.Assert(cfg.BootstrapTimeout, gc.Equals, time.Second*1200)
    35  	c.Assert(cfg.BootstrapRetryDelay, gc.Equals, time.Second*5)
    36  	c.Assert(cfg.BootstrapAddressesDelay, gc.Equals, time.Second*10)
    37  }
    38  
    39  func (*ConfigSuite) TestConfigValuesSpecified(c *gc.C) {
    40  	for _, serviceType := range []string{"external", "loadbalancer"} {
    41  		externalIps := []string{"10.0.0.1", "10.0.0.2"}
    42  		if serviceType == "loadbalancer" {
    43  			externalIps = externalIps[:1]
    44  		}
    45  		cfg, err := bootstrap.NewConfig(map[string]interface{}{
    46  			"admin-secret":              "sekrit",
    47  			"ca-cert":                   testing.CACert,
    48  			"ca-private-key":            testing.CAKey,
    49  			"bootstrap-timeout":         1,
    50  			"bootstrap-retry-delay":     2,
    51  			"bootstrap-addresses-delay": 3,
    52  			"controller-service-type":   serviceType,
    53  			"controller-external-name":  "externalName",
    54  			"controller-external-ips":   externalIps,
    55  		})
    56  		c.Assert(err, jc.ErrorIsNil)
    57  
    58  		c.Assert(cfg, jc.DeepEquals, bootstrap.Config{
    59  			AdminSecret:             "sekrit",
    60  			CACert:                  testing.CACert,
    61  			CAPrivateKey:            testing.CAKey,
    62  			BootstrapTimeout:        time.Second * 1,
    63  			BootstrapRetryDelay:     time.Second * 2,
    64  			BootstrapAddressesDelay: time.Second * 3,
    65  			ControllerServiceType:   serviceType,
    66  			ControllerExternalName:  "externalName",
    67  			ControllerExternalIPs:   externalIps,
    68  		})
    69  	}
    70  }
    71  
    72  func (s *ConfigSuite) addFiles(c *gc.C, files ...gitjujutesting.TestFile) {
    73  	for _, f := range files {
    74  		err := os.WriteFile(osenv.JujuXDGDataHomePath(f.Name), []byte(f.Data), 0666)
    75  		c.Assert(err, gc.IsNil)
    76  	}
    77  }
    78  
    79  func (s *ConfigSuite) TestDefaultConfigReadsDefaultCACertKeyFiles(c *gc.C) {
    80  	s.addFiles(c, []gitjujutesting.TestFile{
    81  		{"ca-cert.pem", testing.CACert},
    82  		{"ca-private-key.pem", testing.CAKey},
    83  	}...)
    84  
    85  	cfg, err := bootstrap.NewConfig(nil)
    86  	c.Assert(err, jc.ErrorIsNil)
    87  
    88  	c.Assert(cfg.CACert, gc.Equals, testing.CACert)
    89  	c.Assert(cfg.CAPrivateKey, gc.Equals, testing.CAKey)
    90  }
    91  
    92  func (s *ConfigSuite) TestConfigReadsCACertKeyFilesFromPaths(c *gc.C) {
    93  	s.addFiles(c, []gitjujutesting.TestFile{
    94  		{"ca-cert-2.pem", testing.OtherCACert},
    95  		{"ca-private-key-2.pem", testing.OtherCAKey},
    96  	}...)
    97  
    98  	cfg, err := bootstrap.NewConfig(map[string]interface{}{
    99  		"ca-cert-path":        "ca-cert-2.pem",
   100  		"ca-private-key-path": "ca-private-key-2.pem",
   101  	})
   102  	c.Assert(err, jc.ErrorIsNil)
   103  
   104  	c.Assert(cfg.CACert, gc.Equals, testing.OtherCACert)
   105  	c.Assert(cfg.CAPrivateKey, gc.Equals, testing.OtherCAKey)
   106  }
   107  
   108  func (s *ConfigSuite) TestConfigNonExistentPath(c *gc.C) {
   109  	s.testConfigError(c, map[string]interface{}{
   110  		"ca-cert-path": "not/there",
   111  	}, `reading "ca-cert" from file: "ca-cert" not set, and could not read from "not/there": .*`)
   112  }
   113  
   114  func (s *ConfigSuite) TestConfigInvalidCACert(c *gc.C) {
   115  	s.testConfigError(c, map[string]interface{}{
   116  		"ca-cert":        invalidCACert,
   117  		"ca-private-key": testing.CAKey,
   118  	}, "validating ca-cert and ca-private-key: x509: malformed certificate")
   119  }
   120  
   121  func (s *ConfigSuite) TestConfigInvalidCAKey(c *gc.C) {
   122  	s.testConfigError(c, map[string]interface{}{
   123  		"ca-cert":        testing.CACert,
   124  		"ca-private-key": invalidCAKey,
   125  	}, "validating ca-cert and ca-private-key: (crypto/)?tls: failed to parse private key")
   126  }
   127  
   128  func (s *ConfigSuite) TestConfigCACertKeyMismatch(c *gc.C) {
   129  	s.testConfigError(c, map[string]interface{}{
   130  		"ca-cert":        testing.CACert,
   131  		"ca-private-key": testing.OtherCAKey,
   132  	}, "validating ca-cert and ca-private-key: (crypto/)?tls: private key does not match public key")
   133  }
   134  
   135  func (s *ConfigSuite) TestConfigCACertWithEmptyKey(c *gc.C) {
   136  	s.testConfigError(c, map[string]interface{}{
   137  		"ca-cert": testing.CACert,
   138  	}, "validating ca-cert and ca-private-key: (crypto/)?tls: failed to find any PEM data in key input")
   139  }
   140  
   141  func (s *ConfigSuite) TestConfigEmptyCACertWithKey(c *gc.C) {
   142  	s.testConfigError(c, map[string]interface{}{
   143  		"ca-private-key": testing.CAKey,
   144  	}, "validating ca-cert and ca-private-key: (crypto/)?tls: failed to find any PEM data in certificate input")
   145  }
   146  
   147  func (*ConfigSuite) testConfigError(c *gc.C, attrs map[string]interface{}, expect string) {
   148  	_, err := bootstrap.NewConfig(attrs)
   149  	c.Assert(err, gc.ErrorMatches, expect)
   150  }
   151  
   152  func (*ConfigSuite) TestValidate(c *gc.C) {
   153  	c.Assert(validConfig().Validate(), jc.ErrorIsNil)
   154  }
   155  
   156  func (*ConfigSuite) TestValidateAdminSecret(c *gc.C) {
   157  	cfg := validConfig()
   158  	cfg.AdminSecret = ""
   159  	c.Assert(cfg.Validate(), gc.ErrorMatches, "empty admin-secret not valid")
   160  }
   161  
   162  func (*ConfigSuite) TestValidateBootstrapTimeout(c *gc.C) {
   163  	cfg := validConfig()
   164  	cfg.BootstrapTimeout = 0
   165  	c.Assert(cfg.Validate(), gc.ErrorMatches, "bootstrap-timeout of 0s? not valid")
   166  }
   167  
   168  func (*ConfigSuite) TestValidateBootstrapRetryDelay(c *gc.C) {
   169  	cfg := validConfig()
   170  	cfg.BootstrapRetryDelay = -1 * time.Second
   171  	c.Assert(cfg.Validate(), gc.ErrorMatches, "bootstrap-retry-delay of -1s not valid")
   172  }
   173  
   174  func (*ConfigSuite) TestValidateBootstrapAddressesDelay(c *gc.C) {
   175  	cfg := validConfig()
   176  	cfg.BootstrapAddressesDelay = -2 * time.Minute
   177  	c.Assert(cfg.Validate(), gc.ErrorMatches, "bootstrap-addresses-delay of -2m0s not valid")
   178  }
   179  
   180  func (*ConfigSuite) TestValidateExternalIpsAndServiceType(c *gc.C) {
   181  	cfg := validConfig()
   182  	cfg.ControllerServiceType = "cluster"
   183  	c.Assert(cfg.Validate(), gc.ErrorMatches, `external IPs require a service type of "external" or "loadbalancer"`)
   184  }
   185  
   186  func (*ConfigSuite) TestValidateExternalIpsAndLoadBalancer(c *gc.C) {
   187  	cfg := validConfig()
   188  	cfg.ControllerServiceType = "loadbalancer"
   189  	c.Assert(cfg.Validate(), gc.ErrorMatches, `only 1 external IP is allowed with service type "loadbalancer"`)
   190  }
   191  
   192  func validConfig() bootstrap.Config {
   193  	return bootstrap.Config{
   194  		AdminSecret:             "sekrit",
   195  		CACert:                  testing.CACert,
   196  		CAPrivateKey:            testing.CAKey,
   197  		BootstrapTimeout:        time.Second * 1,
   198  		BootstrapRetryDelay:     time.Second * 2,
   199  		BootstrapAddressesDelay: time.Second * 3,
   200  		ControllerServiceType:   "external",
   201  		ControllerExternalIPs:   []string{"10.0.0.1", "10.0.0.2"},
   202  	}
   203  }
   204  
   205  var invalidCAKey = `
   206  -----BEGIN RSA PRIVATE KEY-----
   207  MIIBOgIBAAJAZabKgKInuOxj5vDWLwHHQtK3/45KB+32D15w94Nt83BmuGxo90lw
   208  -----END RSA PRIVATE KEY-----
   209  `[1:]
   210  
   211  var invalidCACert = `
   212  -----BEGIN CERTIFICATE-----
   213  MIIBOgIBAAJAZabKgKInuOxj5vDWLwHHQtK3/45KB+32D15w94Nt83BmuGxo90lw
   214  -----END CERTIFICATE-----
   215  `[1:]