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 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") }
// 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 (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 }
// 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()) }
func makeFakeTools(vers version.Binary) ([]byte, string) { return coretesting.TarGz( coretesting.NewTarFile(names.Jujud, 0777, "jujud contents "+vers.String())) }
// 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()) }
// 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 }
// 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()) }