github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/os/series"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/arch"
    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.MustHostSeries(),
    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.Model}, 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.Model}, 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  	for i, test := range []struct {
   160  		agentStreamRequested string
   161  		agentStreamsUsed     []string
   162  	}{{
   163  		agentStreamsUsed: []string{"released"},
   164  	}, {
   165  		agentStreamRequested: "pretend",
   166  		agentStreamsUsed:     []string{"pretend"},
   167  	}} {
   168  		c.Logf("test %d", i)
   169  		envtoolsList := coretools.List{
   170  			&coretools.Tools{
   171  				Version: version.MustParseBinary("123.456.0-win81-alpha"),
   172  				Size:    2048,
   173  				SHA256:  "badf00d",
   174  			},
   175  			&coretools.Tools{
   176  				Version: version.MustParseBinary("123.456.1-win81-alpha"),
   177  			},
   178  		}
   179  		storageMetadata := []binarystorage.Metadata{{
   180  			Version: "123.456.0-win81-alpha",
   181  			Size:    1024,
   182  			SHA256:  "feedface",
   183  		}}
   184  
   185  		s.PatchValue(common.EnvtoolsFindTools, func(e environs.BootstrapEnviron, major, minor int, streams []string, filter coretools.Filter) (coretools.List, error) {
   186  			c.Assert(major, gc.Equals, 123)
   187  			c.Assert(minor, gc.Equals, 456)
   188  			c.Assert(streams, gc.DeepEquals, test.agentStreamsUsed)
   189  			c.Assert(filter.Series, gc.Equals, "win81")
   190  			c.Assert(filter.Arch, gc.Equals, "alpha")
   191  			return envtoolsList, nil
   192  		})
   193  		toolsFinder := common.NewToolsFinder(
   194  			stateenvirons.EnvironConfigGetter{s.State, s.Model}, &mockToolsStorage{metadata: storageMetadata}, sprintfURLGetter("tools:%s"),
   195  		)
   196  		result, err := toolsFinder.FindTools(params.FindToolsParams{
   197  			MajorVersion: 123,
   198  			MinorVersion: 456,
   199  			Series:       "win81",
   200  			Arch:         "alpha",
   201  			AgentStream:  test.agentStreamRequested,
   202  		})
   203  		c.Assert(err, jc.ErrorIsNil)
   204  		c.Assert(result.Error, gc.IsNil)
   205  		c.Check(result.List, jc.DeepEquals, coretools.List{
   206  			&coretools.Tools{
   207  				Version: version.MustParseBinary(storageMetadata[0].Version),
   208  				Size:    storageMetadata[0].Size,
   209  				SHA256:  storageMetadata[0].SHA256,
   210  				URL:     "tools:" + storageMetadata[0].Version,
   211  			},
   212  			&coretools.Tools{
   213  				Version: version.MustParseBinary("123.456.1-win81-alpha"),
   214  				URL:     "tools:123.456.1-win81-alpha",
   215  			},
   216  		})
   217  	}
   218  }
   219  
   220  func (s *toolsSuite) TestFindToolsNotFound(c *gc.C) {
   221  	s.PatchValue(common.EnvtoolsFindTools, func(e environs.BootstrapEnviron, major, minor int, stream []string, filter coretools.Filter) (list coretools.List, err error) {
   222  		return nil, errors.NotFoundf("tools")
   223  	})
   224  	toolsFinder := common.NewToolsFinder(stateenvirons.EnvironConfigGetter{s.State, s.Model}, s.State, sprintfURLGetter("%s"))
   225  	result, err := toolsFinder.FindTools(params.FindToolsParams{})
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	c.Assert(result.Error, jc.Satisfies, params.IsCodeNotFound)
   228  }
   229  
   230  func (s *toolsSuite) TestFindToolsExactInStorage(c *gc.C) {
   231  	mockToolsStorage := &mockToolsStorage{
   232  		metadata: []binarystorage.Metadata{
   233  			{Version: "1.22-beta1-trusty-amd64"},
   234  			{Version: "1.22.0-trusty-amd64"},
   235  		},
   236  	}
   237  
   238  	s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })
   239  	s.PatchValue(&series.MustHostSeries, func() string { return "trusty" })
   240  	s.PatchValue(&jujuversion.Current, version.MustParseBinary("1.22-beta1-trusty-amd64").Number)
   241  	s.testFindToolsExact(c, mockToolsStorage, true, true)
   242  	s.PatchValue(&jujuversion.Current, version.MustParseBinary("1.22.0-trusty-amd64").Number)
   243  	s.testFindToolsExact(c, mockToolsStorage, true, false)
   244  }
   245  
   246  func (s *toolsSuite) TestFindToolsExactNotInStorage(c *gc.C) {
   247  	mockToolsStorage := &mockToolsStorage{}
   248  	s.PatchValue(&jujuversion.Current, version.MustParse("1.22-beta1"))
   249  	s.testFindToolsExact(c, mockToolsStorage, false, true)
   250  	s.PatchValue(&jujuversion.Current, version.MustParse("1.22.0"))
   251  	s.testFindToolsExact(c, mockToolsStorage, false, false)
   252  }
   253  
   254  func (s *toolsSuite) testFindToolsExact(c *gc.C, t common.ToolsStorageGetter, inStorage bool, develVersion bool) {
   255  	var called bool
   256  	s.PatchValue(common.EnvtoolsFindTools, func(e environs.BootstrapEnviron, major, minor int, stream []string, filter coretools.Filter) (list coretools.List, err error) {
   257  		called = true
   258  		c.Assert(filter.Number, gc.Equals, jujuversion.Current)
   259  		c.Assert(filter.Series, gc.Equals, series.MustHostSeries())
   260  		c.Assert(filter.Arch, gc.Equals, arch.HostArch())
   261  		if develVersion {
   262  			c.Assert(stream, gc.DeepEquals, []string{"devel", "proposed", "released"})
   263  		} else {
   264  			c.Assert(stream, gc.DeepEquals, []string{"released"})
   265  		}
   266  		return nil, errors.NotFoundf("tools")
   267  	})
   268  	toolsFinder := common.NewToolsFinder(stateenvirons.EnvironConfigGetter{s.State, s.Model}, t, sprintfURLGetter("tools:%s"))
   269  	result, err := toolsFinder.FindTools(params.FindToolsParams{
   270  		Number:       jujuversion.Current,
   271  		MajorVersion: -1,
   272  		MinorVersion: -1,
   273  		Series:       series.MustHostSeries(),
   274  		Arch:         arch.HostArch(),
   275  	})
   276  	c.Assert(err, jc.ErrorIsNil)
   277  	if inStorage {
   278  		c.Assert(result.Error, gc.IsNil)
   279  		c.Assert(called, jc.IsFalse)
   280  	} else {
   281  		c.Assert(result.Error, gc.ErrorMatches, "tools not found")
   282  		c.Assert(called, jc.IsTrue)
   283  	}
   284  }
   285  
   286  func (s *toolsSuite) TestFindToolsToolsStorageError(c *gc.C) {
   287  	var called bool
   288  	s.PatchValue(common.EnvtoolsFindTools, func(e environs.BootstrapEnviron, major, minor int, stream []string, filter coretools.Filter) (list coretools.List, err error) {
   289  		called = true
   290  		return nil, errors.NotFoundf("tools")
   291  	})
   292  	toolsFinder := common.NewToolsFinder(stateenvirons.EnvironConfigGetter{s.State, s.Model}, &mockToolsStorage{
   293  		err: errors.New("AllMetadata failed"),
   294  	}, sprintfURLGetter("tools:%s"))
   295  	result, err := toolsFinder.FindTools(params.FindToolsParams{
   296  		MajorVersion: 1,
   297  		MinorVersion: -1,
   298  	})
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	// ToolsStorage errors always cause FindTools to bail. Only
   301  	// if AllMetadata succeeds but returns nothing that matches
   302  	// do we continue on to searching simplestreams.
   303  	c.Assert(result.Error, gc.ErrorMatches, "AllMetadata failed")
   304  	c.Assert(called, jc.IsFalse)
   305  }
   306  
   307  func (s *toolsSuite) TestToolsURLGetterNoAPIHostPorts(c *gc.C) {
   308  	g := common.NewToolsURLGetter("my-uuid", mockAPIHostPortsGetter{})
   309  	_, err := g.ToolsURLs(current)
   310  	c.Assert(err, gc.ErrorMatches, "no suitable API server address to pick from")
   311  }
   312  
   313  func (s *toolsSuite) TestToolsURLGetterAPIHostPortsError(c *gc.C) {
   314  	g := common.NewToolsURLGetter("my-uuid", mockAPIHostPortsGetter{err: errors.New("oh noes")})
   315  	_, err := g.ToolsURLs(current)
   316  	c.Assert(err, gc.ErrorMatches, "oh noes")
   317  }
   318  
   319  func (s *toolsSuite) TestToolsURLGetter(c *gc.C) {
   320  	g := common.NewToolsURLGetter("my-uuid", mockAPIHostPortsGetter{
   321  		hostPorts: [][]network.HostPort{
   322  			network.NewHostPorts(1234, "0.1.2.3"),
   323  		},
   324  	})
   325  	urls, err := g.ToolsURLs(current)
   326  	c.Assert(err, jc.ErrorIsNil)
   327  	c.Check(urls, jc.DeepEquals, []string{
   328  		"https://0.1.2.3:1234/model/my-uuid/tools/" + current.String(),
   329  	})
   330  }
   331  
   332  type sprintfURLGetter string
   333  
   334  func (s sprintfURLGetter) ToolsURLs(v version.Binary) ([]string, error) {
   335  	return []string{fmt.Sprintf(string(s), v)}, nil
   336  }
   337  
   338  type mockAPIHostPortsGetter struct {
   339  	hostPorts [][]network.HostPort
   340  	err       error
   341  }
   342  
   343  func (g mockAPIHostPortsGetter) APIHostPortsForAgents() ([][]network.HostPort, error) {
   344  	return g.hostPorts, g.err
   345  }
   346  
   347  type mockToolsStorage struct {
   348  	binarystorage.Storage
   349  	metadata []binarystorage.Metadata
   350  	err      error
   351  }
   352  
   353  func (s *mockToolsStorage) ToolsStorage() (binarystorage.StorageCloser, error) {
   354  	return s, nil
   355  }
   356  
   357  func (s *mockToolsStorage) Close() error {
   358  	return nil
   359  }
   360  
   361  func (s *mockToolsStorage) AllMetadata() ([]binarystorage.Metadata, error) {
   362  	return s.metadata, s.err
   363  }