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: ¶ms.Version{ 115 Version: current, 116 }, 117 }, { 118 Tag: "machine-1", 119 Tools: ¶ms.Version{ 120 Version: current, 121 }, 122 }, { 123 Tag: "machine-42", 124 Tools: ¶ms.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: ¶ms.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 }