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