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: ¶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 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 }