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