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: ¶ms.Version{ 110 Version: current, 111 }, 112 }, { 113 Tag: "machine-1", 114 Tools: ¶ms.Version{ 115 Version: current, 116 }, 117 }, { 118 Tag: "machine-42", 119 Tools: ¶ms.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: ¶ms.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 }