github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/openstack/provider_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package openstack
     5  
     6  import (
     7  	gitjujutesting "github.com/juju/testing"
     8  	jc "github.com/juju/testing/checkers"
     9  	gc "gopkg.in/check.v1"
    10  	"gopkg.in/goose.v1/nova"
    11  
    12  	"github.com/juju/juju/cloud"
    13  	"github.com/juju/juju/environs"
    14  	"github.com/juju/juju/network"
    15  )
    16  
    17  // localTests contains tests which do not require a live service or test double to run.
    18  type localTests struct {
    19  	gitjujutesting.IsolationSuite
    20  }
    21  
    22  var _ = gc.Suite(&localTests{})
    23  
    24  // ported from lp:juju/juju/providers/openstack/tests/test_machine.py
    25  var addressTests = []struct {
    26  	summary    string
    27  	floatingIP string
    28  	private    []nova.IPAddress
    29  	public     []nova.IPAddress
    30  	networks   []string
    31  	expected   string
    32  	failure    error
    33  }{{
    34  	summary:  "missing",
    35  	expected: "",
    36  }, {
    37  	summary:  "empty",
    38  	private:  []nova.IPAddress{},
    39  	networks: []string{"private"},
    40  	expected: "",
    41  }, {
    42  	summary:  "private IPv4 only",
    43  	private:  []nova.IPAddress{{4, "192.168.0.1"}},
    44  	networks: []string{"private"},
    45  	expected: "192.168.0.1",
    46  }, {
    47  	summary:  "private IPv6 only",
    48  	private:  []nova.IPAddress{{6, "fc00::1"}},
    49  	networks: []string{"private"},
    50  	expected: "fc00::1",
    51  }, {
    52  	summary:  "private only, both IPv4 and IPv6",
    53  	private:  []nova.IPAddress{{4, "192.168.0.1"}, {6, "fc00::1"}},
    54  	networks: []string{"private"},
    55  	expected: "192.168.0.1",
    56  }, {
    57  	summary:  "private only, both IPv6 and IPv4",
    58  	private:  []nova.IPAddress{{6, "fc00::1"}, {4, "192.168.0.1"}},
    59  	networks: []string{"private"},
    60  	expected: "fc00::1",
    61  }, {
    62  	summary:  "private IPv4 plus (HP cloud)",
    63  	private:  []nova.IPAddress{{4, "10.0.0.1"}, {4, "8.8.4.4"}},
    64  	networks: []string{"private"},
    65  	expected: "8.8.4.4",
    66  }, {
    67  	summary:  "public IPv4 only",
    68  	public:   []nova.IPAddress{{4, "8.8.8.8"}},
    69  	networks: []string{"", "public"},
    70  	expected: "8.8.8.8",
    71  }, {
    72  	summary:  "public IPv6 only",
    73  	public:   []nova.IPAddress{{6, "2001:db8::1"}},
    74  	networks: []string{"", "public"},
    75  	expected: "2001:db8::1",
    76  }, {
    77  	summary:  "public only, both IPv4 and IPv6",
    78  	public:   []nova.IPAddress{{4, "8.8.8.8"}, {6, "2001:db8::1"}},
    79  	networks: []string{"", "public"},
    80  	expected: "8.8.8.8",
    81  }, {
    82  	summary:  "public only, both IPv6 and IPv4",
    83  	public:   []nova.IPAddress{{6, "2001:db8::1"}, {4, "8.8.8.8"}},
    84  	networks: []string{"", "public"},
    85  	expected: "2001:db8::1",
    86  }, {
    87  	summary:  "public and private both IPv4",
    88  	private:  []nova.IPAddress{{4, "10.0.0.4"}},
    89  	public:   []nova.IPAddress{{4, "8.8.4.4"}},
    90  	networks: []string{"private", "public"},
    91  	expected: "8.8.4.4",
    92  }, {
    93  	summary:  "public and private both IPv6",
    94  	private:  []nova.IPAddress{{6, "fc00::1"}},
    95  	public:   []nova.IPAddress{{6, "2001:db8::1"}},
    96  	networks: []string{"private", "public"},
    97  	expected: "2001:db8::1",
    98  }, {
    99  	summary:  "public, private, and localhost IPv4",
   100  	private:  []nova.IPAddress{{4, "127.0.0.4"}, {4, "192.168.0.1"}},
   101  	public:   []nova.IPAddress{{4, "8.8.8.8"}},
   102  	networks: []string{"private", "public"},
   103  	expected: "8.8.8.8",
   104  }, {
   105  	summary:  "public, private, and localhost IPv6",
   106  	private:  []nova.IPAddress{{6, "::1"}, {6, "fc00::1"}},
   107  	public:   []nova.IPAddress{{6, "2001:db8::1"}},
   108  	networks: []string{"private", "public"},
   109  	expected: "2001:db8::1",
   110  }, {
   111  	summary:  "public, private, and localhost - both IPv4 and IPv6",
   112  	private:  []nova.IPAddress{{4, "127.0.0.4"}, {4, "192.168.0.1"}, {6, "::1"}, {6, "fc00::1"}},
   113  	public:   []nova.IPAddress{{4, "8.8.8.8"}, {6, "2001:db8::1"}},
   114  	networks: []string{"private", "public"},
   115  	expected: "8.8.8.8",
   116  }, {
   117  	summary:  "public, private, and localhost - both IPv6 and IPv4",
   118  	private:  []nova.IPAddress{{6, "::1"}, {6, "fc00::1"}, {4, "127.0.0.4"}, {4, "192.168.0.1"}},
   119  	public:   []nova.IPAddress{{6, "2001:db8::1"}, {4, "8.8.8.8"}},
   120  	networks: []string{"private", "public"},
   121  	expected: "2001:db8::1",
   122  }, {
   123  	summary:  "custom only IPv4",
   124  	private:  []nova.IPAddress{{4, "192.168.0.1"}},
   125  	networks: []string{"special"},
   126  	expected: "192.168.0.1",
   127  }, {
   128  	summary:  "custom only IPv6",
   129  	private:  []nova.IPAddress{{6, "fc00::1"}},
   130  	networks: []string{"special"},
   131  	expected: "fc00::1",
   132  }, {
   133  	summary:  "custom only - both IPv4 and IPv6",
   134  	private:  []nova.IPAddress{{4, "192.168.0.1"}, {6, "fc00::1"}},
   135  	networks: []string{"special"},
   136  	expected: "192.168.0.1",
   137  }, {
   138  	summary:  "custom only - both IPv6 and IPv4",
   139  	private:  []nova.IPAddress{{6, "fc00::1"}, {4, "192.168.0.1"}},
   140  	networks: []string{"special"},
   141  	expected: "fc00::1",
   142  }, {
   143  	summary:  "custom and public IPv4",
   144  	private:  []nova.IPAddress{{4, "172.16.0.1"}},
   145  	public:   []nova.IPAddress{{4, "8.8.8.8"}},
   146  	networks: []string{"special", "public"},
   147  	expected: "8.8.8.8",
   148  }, {
   149  	summary:  "custom and public IPv6",
   150  	private:  []nova.IPAddress{{6, "fc00::1"}},
   151  	public:   []nova.IPAddress{{6, "2001:db8::1"}},
   152  	networks: []string{"special", "public"},
   153  	expected: "2001:db8::1",
   154  }, {
   155  	summary:  "custom and public - both IPv4 and IPv6",
   156  	private:  []nova.IPAddress{{4, "172.16.0.1"}, {6, "fc00::1"}},
   157  	public:   []nova.IPAddress{{4, "8.8.8.8"}, {6, "2001:db8::1"}},
   158  	networks: []string{"special", "public"},
   159  	expected: "8.8.8.8",
   160  }, {
   161  	summary:  "custom and public - both IPv6 and IPv4",
   162  	private:  []nova.IPAddress{{6, "fc00::1"}, {4, "172.16.0.1"}},
   163  	public:   []nova.IPAddress{{6, "2001:db8::1"}, {4, "8.8.8.8"}},
   164  	networks: []string{"special", "public"},
   165  	expected: "2001:db8::1",
   166  }, {
   167  	summary:    "floating and public, same address",
   168  	floatingIP: "8.8.8.8",
   169  	public:     []nova.IPAddress{{4, "8.8.8.8"}},
   170  	networks:   []string{"", "public"},
   171  	expected:   "8.8.8.8",
   172  }, {
   173  	summary:    "floating and public, different address",
   174  	floatingIP: "8.8.4.4",
   175  	public:     []nova.IPAddress{{4, "8.8.8.8"}},
   176  	networks:   []string{"", "public"},
   177  	expected:   "8.8.4.4",
   178  }, {
   179  	summary:    "floating and private",
   180  	floatingIP: "8.8.4.4",
   181  	private:    []nova.IPAddress{{4, "10.0.0.1"}},
   182  	networks:   []string{"private"},
   183  	expected:   "8.8.4.4",
   184  }, {
   185  	summary:    "floating, custom and public",
   186  	floatingIP: "8.8.4.4",
   187  	private:    []nova.IPAddress{{4, "172.16.0.1"}},
   188  	public:     []nova.IPAddress{{4, "8.8.8.8"}},
   189  	networks:   []string{"special", "public"},
   190  	expected:   "8.8.4.4",
   191  }}
   192  
   193  func (t *localTests) TestGetServerAddresses(c *gc.C) {
   194  	for i, t := range addressTests {
   195  		c.Logf("#%d. %s -> %s (%v)", i, t.summary, t.expected, t.failure)
   196  		addresses := make(map[string][]nova.IPAddress)
   197  		if t.private != nil {
   198  			if len(t.networks) < 1 {
   199  				addresses["private"] = t.private
   200  			} else {
   201  				addresses[t.networks[0]] = t.private
   202  			}
   203  		}
   204  		if t.public != nil {
   205  			if len(t.networks) < 2 {
   206  				addresses["public"] = t.public
   207  			} else {
   208  				addresses[t.networks[1]] = t.public
   209  			}
   210  		}
   211  		addr := InstanceAddress(t.floatingIP, addresses)
   212  		c.Assert(addr, gc.Equals, t.expected)
   213  	}
   214  }
   215  
   216  func (*localTests) TestPortsToRuleInfo(c *gc.C) {
   217  	groupId := "groupid"
   218  	testCases := []struct {
   219  		about    string
   220  		ports    []network.PortRange
   221  		expected []nova.RuleInfo
   222  	}{{
   223  		about: "single port",
   224  		ports: []network.PortRange{{
   225  			FromPort: 80,
   226  			ToPort:   80,
   227  			Protocol: "tcp",
   228  		}},
   229  		expected: []nova.RuleInfo{{
   230  			IPProtocol:    "tcp",
   231  			FromPort:      80,
   232  			ToPort:        80,
   233  			Cidr:          "0.0.0.0/0",
   234  			ParentGroupId: groupId,
   235  		}},
   236  	}, {
   237  		about: "multiple ports",
   238  		ports: []network.PortRange{{
   239  			FromPort: 80,
   240  			ToPort:   82,
   241  			Protocol: "tcp",
   242  		}},
   243  		expected: []nova.RuleInfo{{
   244  			IPProtocol:    "tcp",
   245  			FromPort:      80,
   246  			ToPort:        82,
   247  			Cidr:          "0.0.0.0/0",
   248  			ParentGroupId: groupId,
   249  		}},
   250  	}, {
   251  		about: "multiple port ranges",
   252  		ports: []network.PortRange{{
   253  			FromPort: 80,
   254  			ToPort:   82,
   255  			Protocol: "tcp",
   256  		}, {
   257  			FromPort: 100,
   258  			ToPort:   120,
   259  			Protocol: "tcp",
   260  		}},
   261  		expected: []nova.RuleInfo{{
   262  			IPProtocol:    "tcp",
   263  			FromPort:      80,
   264  			ToPort:        82,
   265  			Cidr:          "0.0.0.0/0",
   266  			ParentGroupId: groupId,
   267  		}, {
   268  			IPProtocol:    "tcp",
   269  			FromPort:      100,
   270  			ToPort:        120,
   271  			Cidr:          "0.0.0.0/0",
   272  			ParentGroupId: groupId,
   273  		}},
   274  	}}
   275  
   276  	for i, t := range testCases {
   277  		c.Logf("test %d: %s", i, t.about)
   278  		rules := PortsToRuleInfo(groupId, t.ports)
   279  		c.Check(len(rules), gc.Equals, len(t.expected))
   280  		c.Check(rules, gc.DeepEquals, t.expected)
   281  	}
   282  }
   283  
   284  func (*localTests) TestRuleMatchesPortRange(c *gc.C) {
   285  	proto_tcp := "tcp"
   286  	proto_udp := "udp"
   287  	port_80 := 80
   288  	port_85 := 85
   289  
   290  	testCases := []struct {
   291  		about    string
   292  		ports    network.PortRange
   293  		rule     nova.SecurityGroupRule
   294  		expected bool
   295  	}{{
   296  		about: "single port",
   297  		ports: network.PortRange{
   298  			FromPort: 80,
   299  			ToPort:   80,
   300  			Protocol: "tcp",
   301  		},
   302  		rule: nova.SecurityGroupRule{
   303  			IPProtocol: &proto_tcp,
   304  			FromPort:   &port_80,
   305  			ToPort:     &port_80,
   306  		},
   307  		expected: true,
   308  	}, {
   309  		about: "multiple port",
   310  		ports: network.PortRange{
   311  			FromPort: port_80,
   312  			ToPort:   port_85,
   313  			Protocol: proto_tcp,
   314  		},
   315  		rule: nova.SecurityGroupRule{
   316  			IPProtocol: &proto_tcp,
   317  			FromPort:   &port_80,
   318  			ToPort:     &port_85,
   319  		},
   320  		expected: true,
   321  	}, {
   322  		about: "nil rule components",
   323  		ports: network.PortRange{
   324  			FromPort: port_80,
   325  			ToPort:   port_85,
   326  			Protocol: proto_tcp,
   327  		},
   328  		rule: nova.SecurityGroupRule{
   329  			IPProtocol: nil,
   330  			FromPort:   nil,
   331  			ToPort:     nil,
   332  		},
   333  		expected: false,
   334  	}, {
   335  		about: "mismatched port range and rule",
   336  		ports: network.PortRange{
   337  			FromPort: port_80,
   338  			ToPort:   port_85,
   339  			Protocol: proto_tcp,
   340  		},
   341  		rule: nova.SecurityGroupRule{
   342  			IPProtocol: &proto_udp,
   343  			FromPort:   &port_80,
   344  			ToPort:     &port_80,
   345  		},
   346  		expected: false,
   347  	}}
   348  	for i, t := range testCases {
   349  		c.Logf("test %d: %s", i, t.about)
   350  		c.Check(RuleMatchesPortRange(t.rule, t.ports), gc.Equals, t.expected)
   351  	}
   352  }
   353  
   354  func (s *localTests) TestDetectRegionsNoRegionName(c *gc.C) {
   355  	_, err := s.detectRegions(c)
   356  	c.Assert(err, gc.ErrorMatches, "OS_REGION_NAME environment variable not set")
   357  }
   358  
   359  func (s *localTests) TestDetectRegionsNoAuthURL(c *gc.C) {
   360  	s.PatchEnvironment("OS_REGION_NAME", "oceania")
   361  	_, err := s.detectRegions(c)
   362  	c.Assert(err, gc.ErrorMatches, "OS_AUTH_URL environment variable not set")
   363  }
   364  
   365  func (s *localTests) TestDetectRegions(c *gc.C) {
   366  	s.PatchEnvironment("OS_REGION_NAME", "oceania")
   367  	s.PatchEnvironment("OS_AUTH_URL", "http://keystone.internal")
   368  	regions, err := s.detectRegions(c)
   369  	c.Assert(err, jc.ErrorIsNil)
   370  	c.Assert(regions, jc.DeepEquals, []cloud.Region{
   371  		{Name: "oceania", Endpoint: "http://keystone.internal"},
   372  	})
   373  }
   374  
   375  func (s *localTests) detectRegions(c *gc.C) ([]cloud.Region, error) {
   376  	provider, err := environs.Provider("openstack")
   377  	c.Assert(err, jc.ErrorIsNil)
   378  	c.Assert(provider, gc.Implements, new(environs.CloudRegionDetector))
   379  	return provider.(environs.CloudRegionDetector).DetectRegions()
   380  }
   381  
   382  type providerUnitTests struct{}
   383  
   384  var _ = gc.Suite(&providerUnitTests{})
   385  
   386  func (s *providerUnitTests) TestIdentityClientVersion_BadURLErrors(c *gc.C) {
   387  	_, err := identityClientVersion("abc123")
   388  	c.Check(err, gc.Not(jc.ErrorIsNil))
   389  }
   390  
   391  func (s *providerUnitTests) TestIdentityClientVersion_ParsesGoodURL(c *gc.C) {
   392  	version, err := identityClientVersion("https://keystone.internal/v2.0")
   393  	c.Assert(err, jc.ErrorIsNil)
   394  	c.Check(version, gc.Equals, 2)
   395  
   396  	version, err = identityClientVersion("https://keystone.internal/v3.0/")
   397  	c.Assert(err, jc.ErrorIsNil)
   398  	c.Check(version, gc.Equals, 3)
   399  
   400  	version, err = identityClientVersion("https://keystone.internal/v2/")
   401  	c.Assert(err, jc.ErrorIsNil)
   402  	c.Check(version, gc.Equals, 2)
   403  }