func (b *buildSuite) TestGetVersionFromJujud(c *gc.C) { ver := version.Binary{ Number: version.Number{ Major: 1, Minor: 2, Tag: "beta", Patch: 1, }, Series: "trusty", Arch: "amd64", } argsCh := make(chan []string, 1) execCommand := b.GetExecCommand(exttest.PatchExecConfig{ Stderr: "hey, here's some logging you should ignore", Stdout: ver.String(), Args: argsCh, }) b.PatchValue(tools.ExecCommand, execCommand) v, err := tools.GetVersionFromJujud("foo") c.Assert(err, jc.ErrorIsNil) c.Assert(v, gc.Equals, ver) select { case args := <-argsCh: cmd := filepath.Join("foo", names.Jujud) c.Assert(args, gc.DeepEquals, []string{cmd, "version"}) default: c.Fatalf("Failed to get args sent to executable.") } }
// GetMockBuildTools returns a sync.BuildAgentTarballFunc implementation which generates // a fake tools tarball. func GetMockBuildTools(c *gc.C) sync.BuildAgentTarballFunc { return func(build bool, forceVersion *version.Number, stream string) (*sync.BuiltAgent, error) { vers := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } if forceVersion != nil { vers.Number = *forceVersion } tgz, checksum := coretesting.TarGz( coretesting.NewTarFile(names.Jujud, 0777, "jujud contents "+vers.String())) toolsDir, err := ioutil.TempDir("", "juju-tools-"+stream) c.Assert(err, jc.ErrorIsNil) name := "name" ioutil.WriteFile(filepath.Join(toolsDir, name), tgz, 0777) return &sync.BuiltAgent{ Dir: toolsDir, StorageName: name, Version: vers, Size: int64(len(tgz)), Sha256Hash: checksum, }, nil } }
func (s *toolsSuite) TestFindAvailableToolsSpecificVersion(c *gc.C) { currentVersion := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } currentVersion.Major = 2 currentVersion.Minor = 3 s.PatchValue(&jujuversion.Current, currentVersion.Number) var findToolsCalled int s.PatchValue(bootstrap.FindTools, func(_ environs.Environ, major, minor int, stream string, f tools.Filter) (tools.List, error) { c.Assert(f.Number.Major, gc.Equals, 10) c.Assert(f.Number.Minor, gc.Equals, 11) c.Assert(f.Number.Patch, gc.Equals, 12) c.Assert(stream, gc.Equals, "released") findToolsCalled++ return []*tools.Tools{ &tools.Tools{ Version: currentVersion, URL: "http://testing.invalid/tools.tar.gz", }, }, nil }) env := newEnviron("foo", useDefaultKeys, nil) toolsVersion := version.MustParse("10.11.12") result, err := bootstrap.FindAvailableTools(env, &toolsVersion, nil, nil, false) c.Assert(err, jc.ErrorIsNil) c.Assert(findToolsCalled, gc.Equals, 1) c.Assert(result, jc.DeepEquals, tools.List{ &tools.Tools{ Version: currentVersion, URL: "http://testing.invalid/tools.tar.gz", }, }) }
func checkVersionOutput(c *gc.C, output string) { ver := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } c.Check(output, gc.Equals, ver.String()+"\n") }
// Return a version the same as the current software version, but with // the build number bumped. // // The version Tag is also cleared so that upgrades.PerformUpgrade // doesn't think it needs to run upgrade steps unnecessarily. func makeBumpedCurrentVersion() version.Binary { v := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } v.Build++ v.Tag = "" return v }
// ValidateToolsMetadata attempts to load tools metadata for the specified cloud attributes and returns // any tools versions found, or an error if the metadata could not be loaded. func ValidateToolsMetadata(params *ToolsMetadataLookupParams) ([]string, *simplestreams.ResolveInfo, error) { if len(params.Architectures) == 0 { return nil, nil, fmt.Errorf("required parameter arches not specified") } if len(params.Sources) == 0 { return nil, nil, fmt.Errorf("required parameter sources not specified") } if params.Version == "" && params.Major == 0 { params.Version = jujuversion.Current.String() } var toolsConstraint *ToolsConstraint if params.Version == "" { toolsConstraint = NewGeneralToolsConstraint(params.Major, params.Minor, simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ Region: params.Region, Endpoint: params.Endpoint, }, Stream: params.Stream, Series: []string{params.Series}, Arches: params.Architectures, }) } else { versNum, err := version.Parse(params.Version) if err != nil { return nil, nil, err } toolsConstraint = NewVersionedToolsConstraint(versNum, simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ Region: params.Region, Endpoint: params.Endpoint, }, Stream: params.Stream, Series: []string{params.Series}, Arches: params.Architectures, }) } matchingTools, resolveInfo, err := Fetch(params.Sources, toolsConstraint) if err != nil { return nil, resolveInfo, err } if len(matchingTools) == 0 { return nil, resolveInfo, fmt.Errorf("no matching tools found for constraint %+v", toolsConstraint) } versions := make([]string, len(matchingTools)) for i, tm := range matchingTools { vers := version.Binary{ Number: version.MustParse(tm.Version), Series: tm.Release, Arch: tm.Arch, } versions[i] = vers.String() } return versions, resolveInfo, nil }
func makeToolsList(series string) tools.List { var toolsVersion version.Binary toolsVersion.Number = version.MustParse("1.26.0") toolsVersion.Arch = arch.AMD64 toolsVersion.Series = series return tools.List{{ Version: toolsVersion, URL: fmt.Sprintf("http://example.com/tools/juju-%s.tgz", toolsVersion), SHA256: "1234567890abcdef", Size: 1024, }} }
func (s *toolsSuite) TestDownloadTopLevelPath(c *gc.C) { v := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } tools := s.storeFakeTools(c, s.State, "abc", binarystorage.Metadata{ Version: v.String(), Size: 3, SHA256: "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", }) s.testDownload(c, tools, "") }
// fetchAndCacheTools fetches tools with the specified version by searching for a URL // in simplestreams and GETting it, caching the result in tools storage before returning // to the caller. func (h *toolsDownloadHandler) fetchAndCacheTools(v version.Binary, stor binarystorage.Storage, st *state.State) (io.ReadCloser, error) { envcfg, err := st.ModelConfig() if err != nil { return nil, err } env, err := environs.New(envcfg) if err != nil { return nil, err } tools, err := envtools.FindExactTools(env, v.Number, v.Series, v.Arch) if err != nil { return nil, err } // No need to verify the server's identity because we verify the SHA-256 hash. logger.Infof("fetching %v tools from %v", v, tools.URL) resp, err := utils.GetNonValidatingHTTPClient().Get(tools.URL) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { msg := fmt.Sprintf("bad HTTP response: %v", resp.Status) if body, err := ioutil.ReadAll(resp.Body); err == nil { msg += fmt.Sprintf(" (%s)", bytes.TrimSpace(body)) } return nil, errors.New(msg) } data, sha256, err := readAndHash(resp.Body) if err != nil { return nil, err } if int64(len(data)) != tools.Size { return nil, errors.Errorf("size mismatch for %s", tools.URL) } if sha256 != tools.SHA256 { return nil, errors.Errorf("hash mismatch for %s", tools.URL) } // Cache tarball in tools storage before returning. metadata := binarystorage.Metadata{ Version: v.String(), Size: tools.Size, SHA256: tools.SHA256, } if err := stor.Add(bytes.NewReader(data), metadata); err != nil { return nil, errors.Annotate(err, "error caching tools") } return ioutil.NopCloser(bytes.NewReader(data)), nil }
func (s *UpgradeJujuSuite) TestUpgradeJujuWithRealUpload(c *gc.C) { s.Reset(c) s.PatchValue(&jujuversion.Current, version.MustParse("1.99.99")) cmd := newUpgradeJujuCommand(map[int]version.Number{2: version.MustParse("1.99.99")}) _, err := coretesting.RunCommand(c, cmd, "--build-agent") c.Assert(err, jc.ErrorIsNil) vers := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } vers.Build = 1 s.checkToolsUploaded(c, vers, vers.Number) }
func GetMockBundleTools(c *gc.C) tools.BundleToolsFunc { return func(w io.Writer, forceVersion *version.Number) (version.Binary, string, error) { vers := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } if forceVersion != nil { vers.Number = *forceVersion } sha256Hash := fmt.Sprintf("%x", sha256.New().Sum(nil)) return vers, sha256Hash, nil } }
// NewSuperCommand is like cmd.NewSuperCommand but // it adds juju-specific functionality: // - The default logging configuration is taken from the environment; // - The version is configured to the current juju version; // - The command emits a log message when a command runs. func NewSuperCommand(p cmd.SuperCommandParams) *cmd.SuperCommand { p.Log = &cmd.Log{ DefaultConfig: os.Getenv(osenv.JujuLoggingConfigEnvKey), } current := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } // p.Version should be a version.Binary, but juju/cmd does not // import juju/juju/version so this cannot happen. We have // tests to assert that this string value is correct. p.Version = current.String() p.NotifyRun = runNotifier return cmd.NewSuperCommand(p) }
func (s *UpgradeJujuSuite) checkToolsUploaded(c *gc.C, vers version.Binary, agentVersion version.Number) { storage, err := s.State.ToolsStorage() c.Assert(err, jc.ErrorIsNil) defer storage.Close() _, r, err := storage.Open(vers.String()) if !c.Check(err, jc.ErrorIsNil) { return } data, err := ioutil.ReadAll(r) r.Close() c.Check(err, jc.ErrorIsNil) expectContent := version.Binary{ Number: agentVersion, Arch: arch.HostArch(), Series: series.HostSeries(), } checkToolsContent(c, data, "jujud contents "+expectContent.String()) }
// RemoveFakeTools deletes the fake tools from the supplied storage. func RemoveFakeTools(c *gc.C, stor storage.Storage, toolsDir string) { c.Logf("removing fake tools") toolsVersion := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } name := envtools.StorageName(toolsVersion, toolsDir) err := stor.Remove(name) c.Check(err, jc.ErrorIsNil) defaultSeries := series.LatestLts() if series.HostSeries() != defaultSeries { toolsVersion.Series = defaultSeries name := envtools.StorageName(toolsVersion, toolsDir) err := stor.Remove(name) c.Check(err, jc.ErrorIsNil) } RemoveFakeToolsMetadata(c, stor) }
// UploadFakeTools uploads fake tools of the architectures in // s.UploadArches for each LTS release to the specified storage. func (s *ToolsFixture) UploadFakeTools(c *gc.C, stor storage.Storage, toolsDir, stream string) { arches := s.UploadArches if len(arches) == 0 { arches = []string{arch.HostArch()} } var versions []version.Binary for _, arch := range arches { v := version.Binary{ Number: jujuversion.Current, Arch: arch, } for _, series := range toolsLtsSeries { v.Series = series versions = append(versions, v) } } c.Logf("uploading fake tool versions: %v", versions) _, err := UploadFakeToolsVersions(stor, toolsDir, stream, versions...) c.Assert(err, jc.ErrorIsNil) }
// SharedToolsDir returns the directory that is used to // store binaries for the given version of the juju tools // within the dataDir directory. func SharedToolsDir(dataDir string, vers version.Binary) string { return path.Join(dataDir, "tools", vers.String()) }
// StorageName returns the name that is used to store and retrieve the // given version of the juju tools. func StorageName(vers version.Binary, stream string) string { return storagePrefix(stream) + vers.String() + toolSuffix }
// ToolsURL returns a tools URL pointing the API server // specified by the "serverRoot". func ToolsURL(serverRoot string, v version.Binary) string { return fmt.Sprintf("%s/tools/%s", serverRoot, v.String()) }
func makeFakeTools(vers version.Binary) ([]byte, string) { return coretesting.TarGz( coretesting.NewTarFile(names.Jujud, 0777, "jujud contents "+vers.String())) }