github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/common/tools_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common_test
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/apiserver/common"
    15  	"github.com/juju/juju/apiserver/params"
    16  	apiservertesting "github.com/juju/juju/apiserver/testing"
    17  	"github.com/juju/juju/environs"
    18  	"github.com/juju/juju/juju/arch"
    19  	"github.com/juju/juju/juju/testing"
    20  	"github.com/juju/juju/network"
    21  	"github.com/juju/juju/state"
    22  	"github.com/juju/juju/state/toolstorage"
    23  	coretools "github.com/juju/juju/tools"
    24  	"github.com/juju/juju/version"
    25  )
    26  
    27  type toolsSuite struct {
    28  	testing.JujuConnSuite
    29  	machine0 *state.Machine
    30  }
    31  
    32  var _ = gc.Suite(&toolsSuite{})
    33  
    34  func (s *toolsSuite) SetUpTest(c *gc.C) {
    35  	s.JujuConnSuite.SetUpTest(c)
    36  	var err error
    37  	s.machine0, err = s.State.AddMachine("series", state.JobHostUnits)
    38  	c.Assert(err, jc.ErrorIsNil)
    39  	s.AddDefaultToolsToState(c)
    40  }
    41  
    42  func (s *toolsSuite) TestTools(c *gc.C) {
    43  	getCanRead := func() (common.AuthFunc, error) {
    44  		return func(tag names.Tag) bool {
    45  			return tag == names.NewMachineTag("0") || tag == names.NewMachineTag("42")
    46  		}, nil
    47  	}
    48  	tg := common.NewToolsGetter(s.State, s.State, s.State, sprintfURLGetter("tools:%s"), getCanRead)
    49  	c.Assert(tg, gc.NotNil)
    50  
    51  	err := s.machine0.SetAgentVersion(version.Current)
    52  	c.Assert(err, jc.ErrorIsNil)
    53  
    54  	args := params.Entities{
    55  		Entities: []params.Entity{
    56  			{Tag: "machine-0"},
    57  			{Tag: "machine-1"},
    58  			{Tag: "machine-42"},
    59  		}}
    60  	result, err := tg.Tools(args)
    61  	c.Assert(err, jc.ErrorIsNil)
    62  	c.Assert(result.Results, gc.HasLen, 3)
    63  	c.Assert(result.Results[0].Error, gc.IsNil)
    64  	c.Assert(result.Results[0].Tools, gc.NotNil)
    65  	c.Assert(result.Results[0].Tools.Version, gc.DeepEquals, version.Current)
    66  	c.Assert(result.Results[0].Tools.URL, gc.Equals, "tools:"+version.Current.String())
    67  	c.Assert(result.Results[0].DisableSSLHostnameVerification, jc.IsTrue)
    68  	c.Assert(result.Results[1].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
    69  	c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.NotFoundError("machine 42"))
    70  }
    71  
    72  func (s *toolsSuite) TestToolsError(c *gc.C) {
    73  	getCanRead := func() (common.AuthFunc, error) {
    74  		return nil, fmt.Errorf("splat")
    75  	}
    76  	tg := common.NewToolsGetter(s.State, s.State, s.State, sprintfURLGetter("%s"), getCanRead)
    77  	args := params.Entities{
    78  		Entities: []params.Entity{{Tag: "machine-42"}},
    79  	}
    80  	result, err := tg.Tools(args)
    81  	c.Assert(err, gc.ErrorMatches, "splat")
    82  	c.Assert(result.Results, gc.HasLen, 1)
    83  }
    84  
    85  func (s *toolsSuite) TestSetTools(c *gc.C) {
    86  	getCanWrite := func() (common.AuthFunc, error) {
    87  		return func(tag names.Tag) bool {
    88  			return tag == names.NewMachineTag("0") || tag == names.NewMachineTag("42")
    89  		}, nil
    90  	}
    91  	ts := common.NewToolsSetter(s.State, getCanWrite)
    92  	c.Assert(ts, gc.NotNil)
    93  
    94  	err := s.machine0.SetAgentVersion(version.Current)
    95  	c.Assert(err, jc.ErrorIsNil)
    96  
    97  	args := params.EntitiesVersion{
    98  		AgentTools: []params.EntityVersion{{
    99  			Tag: "machine-0",
   100  			Tools: &params.Version{
   101  				Version: version.Current,
   102  			},
   103  		}, {
   104  			Tag: "machine-1",
   105  			Tools: &params.Version{
   106  				Version: version.Current,
   107  			},
   108  		}, {
   109  			Tag: "machine-42",
   110  			Tools: &params.Version{
   111  				Version: version.Current,
   112  			},
   113  		}},
   114  	}
   115  	result, err := ts.SetTools(args)
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	c.Assert(result.Results, gc.HasLen, 3)
   118  	c.Assert(result.Results[0].Error, gc.IsNil)
   119  	agentTools, err := s.machine0.AgentTools()
   120  	c.Assert(err, jc.ErrorIsNil)
   121  	c.Assert(agentTools.Version, gc.DeepEquals, version.Current)
   122  	c.Assert(result.Results[1].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
   123  	c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.NotFoundError("machine 42"))
   124  }
   125  
   126  func (s *toolsSuite) TestToolsSetError(c *gc.C) {
   127  	getCanWrite := func() (common.AuthFunc, error) {
   128  		return nil, fmt.Errorf("splat")
   129  	}
   130  	ts := common.NewToolsSetter(s.State, getCanWrite)
   131  	args := params.EntitiesVersion{
   132  		AgentTools: []params.EntityVersion{{
   133  			Tag: "machine-42",
   134  			Tools: &params.Version{
   135  				Version: version.Current,
   136  			},
   137  		}},
   138  	}
   139  	result, err := ts.SetTools(args)
   140  	c.Assert(err, gc.ErrorMatches, "splat")
   141  	c.Assert(result.Results, gc.HasLen, 1)
   142  }
   143  
   144  func (s *toolsSuite) TestFindTools(c *gc.C) {
   145  	envtoolsList := coretools.List{
   146  		&coretools.Tools{
   147  			Version: version.MustParseBinary("123.456.0-win81-alpha"),
   148  			Size:    2048,
   149  			SHA256:  "badf00d",
   150  		},
   151  		&coretools.Tools{
   152  			Version: version.MustParseBinary("123.456.1-win81-alpha"),
   153  		},
   154  	}
   155  	storageMetadata := []toolstorage.Metadata{{
   156  		Version: version.MustParseBinary("123.456.0-win81-alpha"),
   157  		Size:    1024,
   158  		SHA256:  "feedface",
   159  	}}
   160  
   161  	s.PatchValue(common.EnvtoolsFindTools, func(e environs.Environ, major, minor int, stream string, filter coretools.Filter) (coretools.List, error) {
   162  		c.Assert(major, gc.Equals, 123)
   163  		c.Assert(minor, gc.Equals, 456)
   164  		c.Assert(stream, gc.Equals, "released")
   165  		c.Assert(filter.Series, gc.Equals, "win81")
   166  		c.Assert(filter.Arch, gc.Equals, "alpha")
   167  		return envtoolsList, nil
   168  	})
   169  	toolsFinder := common.NewToolsFinder(s.State, &mockToolsStorage{metadata: storageMetadata}, sprintfURLGetter("tools:%s"))
   170  	result, err := toolsFinder.FindTools(params.FindToolsParams{
   171  		MajorVersion: 123,
   172  		MinorVersion: 456,
   173  		Series:       "win81",
   174  		Arch:         "alpha",
   175  	})
   176  	c.Assert(err, jc.ErrorIsNil)
   177  	c.Assert(result.Error, gc.IsNil)
   178  	c.Assert(result.List, gc.DeepEquals, coretools.List{
   179  		&coretools.Tools{
   180  			Version: storageMetadata[0].Version,
   181  			Size:    storageMetadata[0].Size,
   182  			SHA256:  storageMetadata[0].SHA256,
   183  			URL:     "tools:" + storageMetadata[0].Version.String(),
   184  		},
   185  		envtoolsList[1],
   186  	})
   187  }
   188  
   189  func (s *toolsSuite) TestFindToolsNotFound(c *gc.C) {
   190  	s.PatchValue(common.EnvtoolsFindTools, func(e environs.Environ, major, minor int, stream string, filter coretools.Filter) (list coretools.List, err error) {
   191  		return nil, errors.NotFoundf("tools")
   192  	})
   193  	toolsFinder := common.NewToolsFinder(s.State, s.State, sprintfURLGetter("%s"))
   194  	result, err := toolsFinder.FindTools(params.FindToolsParams{})
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	c.Assert(result.Error, jc.Satisfies, params.IsCodeNotFound)
   197  }
   198  
   199  func (s *toolsSuite) TestFindToolsExactInStorage(c *gc.C) {
   200  	mockToolsStorage := &mockToolsStorage{
   201  		metadata: []toolstorage.Metadata{
   202  			{Version: version.MustParseBinary("1.22-beta1-trusty-amd64")},
   203  			{Version: version.MustParseBinary("1.22.0-trusty-amd64")},
   204  		},
   205  	}
   206  
   207  	s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })
   208  	s.PatchValue(&version.Current, version.MustParseBinary("1.22-beta1-trusty-amd64"))
   209  	s.testFindToolsExact(c, mockToolsStorage, true, true)
   210  	s.PatchValue(&version.Current, version.MustParseBinary("1.22.0-trusty-amd64"))
   211  	s.testFindToolsExact(c, mockToolsStorage, true, false)
   212  }
   213  
   214  func (s *toolsSuite) TestFindToolsExactNotInStorage(c *gc.C) {
   215  	mockToolsStorage := &mockToolsStorage{}
   216  	s.PatchValue(&version.Current.Number, version.MustParse("1.22-beta1"))
   217  	s.testFindToolsExact(c, mockToolsStorage, false, true)
   218  	s.PatchValue(&version.Current.Number, version.MustParse("1.22.0"))
   219  	s.testFindToolsExact(c, mockToolsStorage, false, false)
   220  }
   221  
   222  func (s *toolsSuite) testFindToolsExact(c *gc.C, t common.ToolsStorageGetter, inStorage bool, develVersion bool) {
   223  	var called bool
   224  	s.PatchValue(common.EnvtoolsFindTools, func(e environs.Environ, major, minor int, stream string, filter coretools.Filter) (list coretools.List, err error) {
   225  		called = true
   226  		c.Assert(filter.Number, gc.Equals, version.Current.Number)
   227  		c.Assert(filter.Series, gc.Equals, version.Current.Series)
   228  		c.Assert(filter.Arch, gc.Equals, arch.HostArch())
   229  		if develVersion {
   230  			c.Assert(stream, gc.Equals, "devel")
   231  		} else {
   232  			c.Assert(stream, gc.Equals, "released")
   233  		}
   234  		return nil, errors.NotFoundf("tools")
   235  	})
   236  	toolsFinder := common.NewToolsFinder(s.State, t, sprintfURLGetter("tools:%s"))
   237  	result, err := toolsFinder.FindTools(params.FindToolsParams{
   238  		Number:       version.Current.Number,
   239  		MajorVersion: -1,
   240  		MinorVersion: -1,
   241  		Series:       version.Current.Series,
   242  		Arch:         arch.HostArch(),
   243  	})
   244  	c.Assert(err, jc.ErrorIsNil)
   245  	if inStorage {
   246  		c.Assert(result.Error, gc.IsNil)
   247  		c.Assert(called, jc.IsFalse)
   248  	} else {
   249  		c.Assert(result.Error, gc.ErrorMatches, "tools not found")
   250  		c.Assert(called, jc.IsTrue)
   251  	}
   252  }
   253  
   254  func (s *toolsSuite) TestFindToolsToolsStorageError(c *gc.C) {
   255  	var called bool
   256  	s.PatchValue(common.EnvtoolsFindTools, func(e environs.Environ, major, minor int, stream string, filter coretools.Filter) (list coretools.List, err error) {
   257  		called = true
   258  		return nil, errors.NotFoundf("tools")
   259  	})
   260  	toolsFinder := common.NewToolsFinder(s.State, &mockToolsStorage{
   261  		err: errors.New("AllMetadata failed"),
   262  	}, sprintfURLGetter("tools:%s"))
   263  	result, err := toolsFinder.FindTools(params.FindToolsParams{
   264  		MajorVersion: 1,
   265  		MinorVersion: -1,
   266  	})
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	// ToolsStorage errors always cause FindTools to bail. Only
   269  	// if AllMetadata succeeds but returns nothing that matches
   270  	// do we continue on to searching simplestreams.
   271  	c.Assert(result.Error, gc.ErrorMatches, "AllMetadata failed")
   272  	c.Assert(called, jc.IsFalse)
   273  }
   274  
   275  func (s *toolsSuite) TestToolsURLGetterNoAPIHostPorts(c *gc.C) {
   276  	g := common.NewToolsURLGetter("my-uuid", mockAPIHostPortsGetter{})
   277  	_, err := g.ToolsURL(version.Current)
   278  	c.Assert(err, gc.ErrorMatches, "no API host ports")
   279  }
   280  
   281  func (s *toolsSuite) TestToolsURLGetterAPIHostPortsError(c *gc.C) {
   282  	g := common.NewToolsURLGetter("my-uuid", mockAPIHostPortsGetter{err: errors.New("oh noes")})
   283  	_, err := g.ToolsURL(version.Current)
   284  	c.Assert(err, gc.ErrorMatches, "oh noes")
   285  }
   286  
   287  func (s *toolsSuite) TestToolsURLGetter(c *gc.C) {
   288  	g := common.NewToolsURLGetter("my-uuid", mockAPIHostPortsGetter{
   289  		hostPorts: [][]network.HostPort{
   290  			network.NewHostPorts(1234, "0.1.2.3"),
   291  		},
   292  	})
   293  	url, err := g.ToolsURL(version.Current)
   294  	c.Assert(err, jc.ErrorIsNil)
   295  	c.Assert(url, gc.Equals, "https://0.1.2.3:1234/environment/my-uuid/tools/"+version.Current.String())
   296  }
   297  
   298  type sprintfURLGetter string
   299  
   300  func (s sprintfURLGetter) ToolsURL(v version.Binary) (string, error) {
   301  	return fmt.Sprintf(string(s), v), nil
   302  }
   303  
   304  type mockAPIHostPortsGetter struct {
   305  	hostPorts [][]network.HostPort
   306  	err       error
   307  }
   308  
   309  func (g mockAPIHostPortsGetter) APIHostPorts() ([][]network.HostPort, error) {
   310  	return g.hostPorts, g.err
   311  }
   312  
   313  type mockToolsStorage struct {
   314  	toolstorage.Storage
   315  	metadata []toolstorage.Metadata
   316  	err      error
   317  }
   318  
   319  func (s *mockToolsStorage) ToolsStorage() (toolstorage.StorageCloser, error) {
   320  	return s, nil
   321  }
   322  
   323  func (s *mockToolsStorage) Close() error {
   324  	return nil
   325  }
   326  
   327  func (s *mockToolsStorage) AllMetadata() ([]toolstorage.Metadata, error) {
   328  	return s.metadata, s.err
   329  }