github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/provider/local/environprovider_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package local_test
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"os/user"
    10  
    11  	"github.com/juju/loggo"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils/packaging/manager"
    15  	"github.com/juju/utils/proxy"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	lxctesting "github.com/juju/juju/container/lxc/testing"
    19  	"github.com/juju/juju/environs"
    20  	"github.com/juju/juju/environs/config"
    21  	envtesting "github.com/juju/juju/environs/testing"
    22  	"github.com/juju/juju/instance"
    23  	"github.com/juju/juju/provider"
    24  	"github.com/juju/juju/provider/local"
    25  	coretesting "github.com/juju/juju/testing"
    26  	jujuos "github.com/juju/utils/os"
    27  )
    28  
    29  type baseProviderSuite struct {
    30  	lxctesting.TestSuite
    31  	restore func()
    32  }
    33  
    34  func (s *baseProviderSuite) SetUpTest(c *gc.C) {
    35  	s.TestSuite.SetUpTest(c)
    36  	loggo.GetLogger("juju.provider.local").SetLogLevel(loggo.TRACE)
    37  	s.restore = local.MockAddressForInterface()
    38  	s.PatchValue(&local.VerifyPrerequisites, func(containerType instance.ContainerType) error {
    39  		return nil
    40  	})
    41  }
    42  
    43  func (s *baseProviderSuite) TearDownTest(c *gc.C) {
    44  	s.restore()
    45  	s.TestSuite.TearDownTest(c)
    46  }
    47  
    48  type prepareSuite struct {
    49  	coretesting.FakeJujuHomeSuite
    50  }
    51  
    52  var _ = gc.Suite(&prepareSuite{})
    53  
    54  func (s *prepareSuite) SetUpTest(c *gc.C) {
    55  	s.FakeJujuHomeSuite.SetUpTest(c)
    56  	loggo.GetLogger("juju.provider.local").SetLogLevel(loggo.TRACE)
    57  	s.PatchEnvironment("http_proxy", "")
    58  	s.PatchEnvironment("HTTP_PROXY", "")
    59  	s.PatchEnvironment("https_proxy", "")
    60  	s.PatchEnvironment("HTTPS_PROXY", "")
    61  	s.PatchEnvironment("ftp_proxy", "")
    62  	s.PatchEnvironment("FTP_PROXY", "")
    63  	s.PatchEnvironment("no_proxy", "")
    64  	s.PatchEnvironment("NO_PROXY", "")
    65  	s.HookCommandOutput(&manager.CommandOutput, nil, nil)
    66  	s.PatchValue(local.CheckLocalPort, func(port int, desc string) error {
    67  		return nil
    68  	})
    69  	restore := local.MockAddressForInterface()
    70  	s.AddCleanup(func(*gc.C) { restore() })
    71  }
    72  
    73  func (s *prepareSuite) TestPrepareCapturesEnvironment(c *gc.C) {
    74  	baseConfig, err := config.New(config.UseDefaults, map[string]interface{}{
    75  		"type": provider.Local,
    76  		"name": "test",
    77  	})
    78  	c.Assert(err, jc.ErrorIsNil)
    79  	provider, err := environs.Provider(provider.Local)
    80  	c.Assert(err, jc.ErrorIsNil)
    81  
    82  	for i, test := range []struct {
    83  		message          string
    84  		extraConfig      map[string]interface{}
    85  		env              map[string]string
    86  		aptOutput        string
    87  		expectedProxy    proxy.Settings
    88  		expectedAptProxy proxy.Settings
    89  	}{{
    90  		message: "nothing set",
    91  	}, {
    92  		message: "grabs proxy from environment",
    93  		env: map[string]string{
    94  			"http_proxy":  "http://user@10.0.0.1",
    95  			"HTTPS_PROXY": "https://user@10.0.0.1",
    96  			"ftp_proxy":   "ftp://user@10.0.0.1",
    97  			"no_proxy":    "localhost,10.0.3.1",
    98  		},
    99  		expectedProxy: proxy.Settings{
   100  			Http:    "http://user@10.0.0.1",
   101  			Https:   "https://user@10.0.0.1",
   102  			Ftp:     "ftp://user@10.0.0.1",
   103  			NoProxy: "localhost,10.0.3.1",
   104  		},
   105  		expectedAptProxy: proxy.Settings{
   106  			Http:  "http://user@10.0.0.1",
   107  			Https: "https://user@10.0.0.1",
   108  			Ftp:   "ftp://user@10.0.0.1",
   109  		},
   110  	}, {
   111  		message: "skips proxy from environment if http-proxy set",
   112  		extraConfig: map[string]interface{}{
   113  			"http-proxy": "http://user@10.0.0.42",
   114  		},
   115  		env: map[string]string{
   116  			"http_proxy":  "http://user@10.0.0.1",
   117  			"HTTPS_PROXY": "https://user@10.0.0.1",
   118  			"ftp_proxy":   "ftp://user@10.0.0.1",
   119  		},
   120  		expectedProxy: proxy.Settings{
   121  			Http: "http://user@10.0.0.42",
   122  		},
   123  		expectedAptProxy: proxy.Settings{
   124  			Http: "http://user@10.0.0.42",
   125  		},
   126  	}, {
   127  		message: "skips proxy from environment if https-proxy set",
   128  		extraConfig: map[string]interface{}{
   129  			"https-proxy": "https://user@10.0.0.42",
   130  		},
   131  		env: map[string]string{
   132  			"http_proxy":  "http://user@10.0.0.1",
   133  			"HTTPS_PROXY": "https://user@10.0.0.1",
   134  			"ftp_proxy":   "ftp://user@10.0.0.1",
   135  		},
   136  		expectedProxy: proxy.Settings{
   137  			Https: "https://user@10.0.0.42",
   138  		},
   139  		expectedAptProxy: proxy.Settings{
   140  			Https: "https://user@10.0.0.42",
   141  		},
   142  	}, {
   143  		message: "skips proxy from environment if ftp-proxy set",
   144  		extraConfig: map[string]interface{}{
   145  			"ftp-proxy": "ftp://user@10.0.0.42",
   146  		},
   147  		env: map[string]string{
   148  			"http_proxy":  "http://user@10.0.0.1",
   149  			"HTTPS_PROXY": "https://user@10.0.0.1",
   150  			"ftp_proxy":   "ftp://user@10.0.0.1",
   151  		},
   152  		expectedProxy: proxy.Settings{
   153  			Ftp: "ftp://user@10.0.0.42",
   154  		},
   155  		expectedAptProxy: proxy.Settings{
   156  			Ftp: "ftp://user@10.0.0.42",
   157  		},
   158  	}, {
   159  		message: "skips proxy from environment if no-proxy set",
   160  		extraConfig: map[string]interface{}{
   161  			"no-proxy": "localhost,10.0.3.1",
   162  		},
   163  		env: map[string]string{
   164  			"http_proxy":  "http://user@10.0.0.1",
   165  			"HTTPS_PROXY": "https://user@10.0.0.1",
   166  			"ftp_proxy":   "ftp://user@10.0.0.1",
   167  		},
   168  		expectedProxy: proxy.Settings{
   169  			NoProxy: "localhost,10.0.3.1",
   170  		},
   171  	}, {
   172  		message: "apt-proxies detected",
   173  		aptOutput: `CommandLine::AsString "apt-config dump";
   174  Acquire::http::Proxy  "10.0.3.1:3142";
   175  Acquire::https::Proxy "";
   176  Acquire::ftp::Proxy "";
   177  Acquire::magic::Proxy "";
   178  `,
   179  		expectedAptProxy: proxy.Settings{
   180  			Http:  "http://10.0.3.1:3142",
   181  			Https: "",
   182  			Ftp:   "",
   183  		},
   184  	}, {
   185  		message: "apt-proxies not used if apt-http-proxy set",
   186  		extraConfig: map[string]interface{}{
   187  			"apt-http-proxy": "http://value-set",
   188  		},
   189  		aptOutput: `CommandLine::AsString "apt-config dump";
   190  Acquire::http::Proxy  "10.0.3.1:3142";
   191  Acquire::https::Proxy "";
   192  Acquire::ftp::Proxy "";
   193  Acquire::magic::Proxy "";
   194  `,
   195  		expectedAptProxy: proxy.Settings{
   196  			Http: "http://value-set",
   197  		},
   198  	}, {
   199  		message: "apt-proxies not used if apt-https-proxy set",
   200  		extraConfig: map[string]interface{}{
   201  			"apt-https-proxy": "https://value-set",
   202  		},
   203  		aptOutput: `CommandLine::AsString "apt-config dump";
   204  Acquire::http::Proxy  "10.0.3.1:3142";
   205  Acquire::https::Proxy "";
   206  Acquire::ftp::Proxy "";
   207  Acquire::magic::Proxy "";
   208  `,
   209  		expectedAptProxy: proxy.Settings{
   210  			Https: "https://value-set",
   211  		},
   212  	}, {
   213  		message: "apt-proxies not used if apt-ftp-proxy set",
   214  		extraConfig: map[string]interface{}{
   215  			"apt-ftp-proxy": "ftp://value-set",
   216  		},
   217  		aptOutput: `CommandLine::AsString "apt-config dump";
   218  Acquire::http::Proxy  "10.0.3.1:3142";
   219  Acquire::https::Proxy "";
   220  Acquire::ftp::Proxy "";
   221  Acquire::magic::Proxy "";
   222  `,
   223  		expectedAptProxy: proxy.Settings{
   224  			Ftp: "ftp://value-set",
   225  		},
   226  	}} {
   227  		c.Logf("\n%v: %s", i, test.message)
   228  		cleanup := []func(){}
   229  		for key, value := range test.env {
   230  			restore := testing.PatchEnvironment(key, value)
   231  			cleanup = append(cleanup, restore)
   232  		}
   233  		_, restore := testing.HookCommandOutput(&manager.CommandOutput, []byte(test.aptOutput), nil)
   234  		cleanup = append(cleanup, restore)
   235  		testConfig := baseConfig
   236  		if test.extraConfig != nil {
   237  			testConfig, err = baseConfig.Apply(test.extraConfig)
   238  			c.Assert(err, jc.ErrorIsNil)
   239  		}
   240  		env, err := provider.PrepareForBootstrap(envtesting.BootstrapContext(c), testConfig)
   241  		c.Assert(err, jc.ErrorIsNil)
   242  
   243  		envConfig := env.Config()
   244  		c.Assert(envConfig.HttpProxy(), gc.Equals, test.expectedProxy.Http)
   245  		c.Assert(envConfig.HttpsProxy(), gc.Equals, test.expectedProxy.Https)
   246  		c.Assert(envConfig.FtpProxy(), gc.Equals, test.expectedProxy.Ftp)
   247  		c.Assert(envConfig.NoProxy(), gc.Equals, test.expectedProxy.NoProxy)
   248  
   249  		if jujuos.HostOS() == jujuos.Ubuntu {
   250  			c.Assert(envConfig.AptHttpProxy(), gc.Equals, test.expectedAptProxy.Http)
   251  			c.Assert(envConfig.AptHttpsProxy(), gc.Equals, test.expectedAptProxy.Https)
   252  			c.Assert(envConfig.AptFtpProxy(), gc.Equals, test.expectedAptProxy.Ftp)
   253  		}
   254  		for _, clean := range cleanup {
   255  			clean()
   256  		}
   257  	}
   258  }
   259  
   260  func (s *prepareSuite) TestPrepareNamespace(c *gc.C) {
   261  	s.PatchValue(local.DetectPackageProxies, func() (proxy.Settings, error) {
   262  		return proxy.Settings{}, nil
   263  	})
   264  	basecfg, err := config.New(config.UseDefaults, map[string]interface{}{
   265  		"type": "local",
   266  		"name": "test",
   267  	})
   268  	provider, err := environs.Provider("local")
   269  	c.Assert(err, jc.ErrorIsNil)
   270  
   271  	type test struct {
   272  		userEnv   string
   273  		userOS    string
   274  		userOSErr error
   275  		namespace string
   276  		err       string
   277  	}
   278  	tests := []test{{
   279  		userEnv:   "someone",
   280  		userOS:    "other",
   281  		namespace: "someone-test",
   282  	}, {
   283  		userOS:    "other",
   284  		namespace: "other-test",
   285  	}, {
   286  		userOSErr: errors.New("oh noes"),
   287  		err:       "failed to determine username for namespace: oh noes",
   288  	}}
   289  
   290  	for i, test := range tests {
   291  		c.Logf("test %d: %v", i, test)
   292  		s.PatchEnvironment("USER", test.userEnv)
   293  		s.PatchValue(local.UserCurrent, func() (*user.User, error) {
   294  			return &user.User{Username: test.userOS}, test.userOSErr
   295  		})
   296  		env, err := provider.PrepareForBootstrap(envtesting.BootstrapContext(c), basecfg)
   297  		if test.err == "" {
   298  			c.Assert(err, jc.ErrorIsNil)
   299  			cfg := env.Config()
   300  			c.Assert(cfg.UnknownAttrs()["namespace"], gc.Equals, test.namespace)
   301  		} else {
   302  			c.Assert(err, gc.ErrorMatches, test.err)
   303  		}
   304  	}
   305  }
   306  
   307  func (s *prepareSuite) TestPrepareProxySSH(c *gc.C) {
   308  	s.PatchValue(local.DetectPackageProxies, func() (proxy.Settings, error) {
   309  		return proxy.Settings{}, nil
   310  	})
   311  	basecfg, err := config.New(config.UseDefaults, map[string]interface{}{
   312  		"type": "local",
   313  		"name": "test",
   314  	})
   315  	provider, err := environs.Provider("local")
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	env, err := provider.PrepareForBootstrap(envtesting.BootstrapContext(c), basecfg)
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	// local provider sets proxy-ssh to false
   320  	c.Assert(env.Config().ProxySSH(), jc.IsFalse)
   321  }
   322  
   323  func (s *prepareSuite) TestProxyLocalhostFix(c *gc.C) {
   324  	basecfg, err := config.New(config.UseDefaults, map[string]interface{}{
   325  		"type": "local",
   326  		"name": "test",
   327  	})
   328  	c.Assert(err, jc.ErrorIsNil)
   329  	provider, err := environs.Provider(provider.Local)
   330  	c.Assert(err, jc.ErrorIsNil)
   331  
   332  	//URL protocol is irrelelvant as we are only interested in it as string
   333  	urlConstruct := "http://%v%v"
   334  	// This value is currently hard-coded in export_test and is called on by this test setup. @see export_test.go MockAddressForInterface()
   335  	expectedBridge := "127.0.0.1"
   336  	for i, test := range urlReplacementTests {
   337  		c.Logf("test %d: %v\n", i, test.message)
   338  
   339  		//construct proxy env attributes based on the test scenario
   340  		proxyAttrValues := map[string]interface{}{}
   341  		for _, anAttrKey := range config.ProxyAttributes {
   342  			proxyAttrValues[anAttrKey] = fmt.Sprintf(urlConstruct, test.url, test.port)
   343  		}
   344  
   345  		//Update env config to include new attributes
   346  		cfg, err := basecfg.Apply(proxyAttrValues)
   347  		c.Assert(err, jc.ErrorIsNil)
   348  		//this call should replace all loopback urls with bridge ip
   349  		env, err := provider.PrepareForBootstrap(envtesting.BootstrapContext(c), cfg)
   350  		c.Assert(err, jc.ErrorIsNil)
   351  
   352  		// verify that correct replacement took place
   353  		envConfig := env.Config().AllAttrs()
   354  		for _, anAttrKey := range config.ProxyAttributes {
   355  			//expected value is either unchanged original
   356  			expectedAttValue := proxyAttrValues[anAttrKey]
   357  			if test.expectChange {
   358  				// or expected value has bridge ip substituted for localhost variations
   359  				expectedAttValue = fmt.Sprintf(urlConstruct, expectedBridge, test.port)
   360  			}
   361  			c.Assert(envConfig[anAttrKey].(string), gc.Equals, expectedAttValue.(string))
   362  		}
   363  	}
   364  }
   365  
   366  type testURL struct {
   367  	message      string
   368  	url          string
   369  	port         string
   370  	expectChange bool
   371  }
   372  
   373  var urlReplacementTests = []testURL{{
   374  	message:      "replace localhost with bridge ip in proxy url",
   375  	url:          "localhost",
   376  	port:         "",
   377  	expectChange: true,
   378  }, {
   379  	message:      "replace localhost:port with bridge ip:port in proxy url",
   380  	url:          "localhost",
   381  	port:         ":8877",
   382  	expectChange: true,
   383  }, {
   384  	message:      "replace 127.2.0.1 with bridge ip in proxy url",
   385  	url:          "127.2.0.1",
   386  	port:         "",
   387  	expectChange: true,
   388  }, {
   389  	message:      "replace 127.2.0.1:port with bridge ip:port in proxy url",
   390  	url:          "127.2.0.1",
   391  	port:         ":8877",
   392  	expectChange: true,
   393  }, {
   394  	message:      "replace [::1]:port with bridge ip:port in proxy url",
   395  	url:          "[::1]",
   396  	port:         ":8877",
   397  	expectChange: true,
   398  }, {
   399  	// Note that http//::1 (without the square brackets)
   400  	// is not a legal URL. See https://www.ietf.org/rfc/rfc2732.txt.
   401  	message:      "replace [::1] with bridge ip in proxy url",
   402  	url:          "[::1]",
   403  	port:         "",
   404  	expectChange: true,
   405  }, {
   406  	message:      "do not replace provided with bridge ip in proxy url",
   407  	url:          "www.google.com",
   408  	port:         "",
   409  	expectChange: false,
   410  }, {
   411  	message:      "do not replace provided:port with bridge ip:port in proxy url",
   412  	url:          "www.google.com",
   413  	port:         ":8877",
   414  	expectChange: false,
   415  }, {
   416  	message:      "lp 1437296 - apt-http-proxy being reset to bridge address when shouldn't",
   417  	url:          "192.168.1.201",
   418  	port:         ":8000",
   419  	expectChange: false,
   420  },
   421  }