func (t *ToolsMetadata) productId() (string, error) { seriesVersion, err := version.SeriesVersion(t.Release) if err != nil { return "", err } return fmt.Sprintf("com.ubuntu.juju:%s:%s", seriesVersion, t.Arch), nil }
func (s *supportedSeriesSuite) TestSeriesVersion(c *gc.C) { vers, err := version.SeriesVersion("precise") if err != nil && err.Error() == `invalid series "precise"` { c.Fatalf(`Unable to lookup series "precise", you may need to: apt-get install distro-info`) } c.Assert(err, gc.IsNil) c.Assert(vers, gc.Equals, "12.04") }
func (s *supportedSeriesWindowsSuite) TestSeriesVersion(c *gc.C) { vers, err := version.SeriesVersion("win8") if err != nil { c.Assert(err, gc.Not(gc.ErrorMatches), `invalid series "win8"`, gc.Commentf(`unable to lookup series "win8"`)) } else { c.Assert(err, jc.ErrorIsNil) } c.Assert(err, jc.ErrorIsNil) c.Assert(vers, gc.Equals, "win8") }
func (tc *testConstraint) Ids() ([]string, error) { version, err := version.SeriesVersion(tc.Series[0]) if err != nil { return nil, err } ids := make([]string, len(tc.Arches)) for i, arch := range tc.Arches { ids[i] = fmt.Sprintf("com.ubuntu.cloud:server:%s:%s", version, arch) } return ids, nil }
func (s *supportedSeriesSuite) TestSeriesVersion(c *gc.C) { // There is no distro-info on Windows or CentOS. if version.Current.OS != version.Ubuntu { c.Skip("This test is only relevant on Ubuntu.") } vers, err := version.SeriesVersion("precise") if err != nil && err.Error() == `invalid series "precise"` { c.Fatalf(`Unable to lookup series "precise", you may need to: apt-get install distro-info`) } c.Assert(err, jc.ErrorIsNil) c.Assert(vers, gc.Equals, "12.04") }
// MergeAndWriteMetadata reads the existing metadata from storage (if any), // and merges it with supplied metadata, writing the resulting metadata is written to storage. func MergeAndWriteMetadata(series string, metadata []*ImageMetadata, cloudSpec *simplestreams.CloudSpec, metadataStore storage.Storage) error { existingMetadata, err := readMetadata(metadataStore) if err != nil { return err } seriesVersion, err := version.SeriesVersion(series) if err != nil { return err } toWrite, allCloudSpec := mergeMetadata(seriesVersion, cloudSpec, metadata, existingMetadata) return writeMetadata(toWrite, allCloudSpec, metadataStore) }
// cloneToolsForSeries copies the built tools tarball into a tarball for the specified // stream and series and generates corresponding metadata. func cloneToolsForSeries(toolsInfo *BuiltTools, stream string, series ...string) error { // Copy the tools to the target storage, recording a Tools struct for each one. var targetTools coretools.List targetTools = append(targetTools, &coretools.Tools{ Version: toolsInfo.Version, Size: toolsInfo.Size, SHA256: toolsInfo.Sha256Hash, }) putTools := func(vers version.Binary) (string, error) { name := envtools.StorageName(vers, stream) src := filepath.Join(toolsInfo.Dir, toolsInfo.StorageName) dest := filepath.Join(toolsInfo.Dir, name) destDir := filepath.Dir(dest) if err := os.MkdirAll(destDir, 0755); err != nil { return "", err } if err := utils.CopyFile(dest, src); err != nil { return "", err } // Append to targetTools the attributes required to write out tools metadata. targetTools = append(targetTools, &coretools.Tools{ Version: vers, Size: toolsInfo.Size, SHA256: toolsInfo.Sha256Hash, }) return name, nil } logger.Debugf("generating tarballs for %v", series) for _, series := range series { _, err := version.SeriesVersion(series) if err != nil { return err } if series != toolsInfo.Version.Series { fakeVersion := toolsInfo.Version fakeVersion.Series = series if _, err := putTools(fakeVersion); err != nil { return err } } } // The tools have been copied to a temp location from which they will be uploaded, // now write out the matching simplestreams metadata so that SyncTools can find them. metadataStore, err := filestorage.NewFileStorageWriter(toolsInfo.Dir) if err != nil { return err } logger.Debugf("generating tools metadata") return envtools.MergeAndWriteMetadata(metadataStore, stream, stream, targetTools, false) }
// Ids generates a string array representing product ids formed similarly to an ISCSI qualified name (IQN). func (tc *ToolsConstraint) Ids() ([]string, error) { var allIds []string for _, series := range tc.Series { version, err := version.SeriesVersion(series) if err != nil { return nil, err } ids := make([]string, len(tc.Arches)) for i, arch := range tc.Arches { ids[i] = fmt.Sprintf("com.ubuntu.juju:%s:%s", version, arch) } allIds = append(allIds, ids...) } return allIds, nil }
// ProductIds generates a string array representing product ids formed similarly to an ISCSI qualified name (IQN). func (ic *ImageConstraint) ProductIds() ([]string, error) { stream := idStream(ic.Stream) nrArches := len(ic.Arches) nrSeries := len(ic.Series) ids := make([]string, nrArches*nrSeries) for i, arch := range ic.Arches { for j, series := range ic.Series { version, err := version.SeriesVersion(series) if err != nil { return nil, err } ids[j*nrArches+i] = fmt.Sprintf("com.ubuntu.cloud%s:server:%s:%s", stream, version, arch) } } return ids, nil }
// UploadTools uploads tools for the specified series and any other relevant series to // the environment storage, after which it sets the agent-version. If forceVersion is true, // we allow uploading even when the agent-version is already set in the environment. func UploadTools(ctx environs.BootstrapContext, env environs.Environ, toolsArch *string, forceVersion bool, bootstrapSeries ...string) error { logger.Infof("checking that upload is possible") // Check the series are valid. for _, series := range bootstrapSeries { if _, err := version.SeriesVersion(series); err != nil { return err } } // See that we are allowed to upload the tools. if err := validateUploadAllowed(env, toolsArch, forceVersion); err != nil { return err } // Make storage interruptible. interrupted := make(chan os.Signal, 1) interruptStorage := make(chan struct{}) ctx.InterruptNotify(interrupted) defer ctx.StopInterruptNotify(interrupted) defer close(interrupted) go func() { defer close(interruptStorage) // closing interrupts all uploads if _, ok := <-interrupted; ok { ctx.Infof("cancelling tools upload") } }() stor := newInterruptibleStorage(env.Storage(), interruptStorage) cfg := env.Config() explicitVersion := uploadVersion(version.Current.Number, nil) uploadSeries := SeriesToUpload(cfg, bootstrapSeries) ctx.Infof("uploading tools for series %s", uploadSeries) tools, err := sync.Upload(stor, &explicitVersion, uploadSeries...) if err != nil { return err } cfg, err = cfg.Apply(map[string]interface{}{ "agent-version": tools.Version.Number.String(), }) if err == nil { err = env.SetConfig(cfg) } if err != nil { return fmt.Errorf("failed to update environment configuration: %v", err) } return nil }
func (s *providerSuite) makeTestMetadata(c *gc.C, series, location string, im []*imagemetadata.ImageMetadata) { cloudSpec := simplestreams.CloudSpec{ Region: location, Endpoint: "https://management.core.windows.net/", } seriesVersion, err := version.SeriesVersion(series) c.Assert(err, jc.ErrorIsNil) for _, im := range im { im.Version = seriesVersion im.RegionName = cloudSpec.Region im.Endpoint = cloudSpec.Endpoint } index, products, err := imagemetadata.MarshalImageMetadataJSON( im, []simplestreams.CloudSpec{cloudSpec}, time.Now(), ) c.Assert(err, jc.ErrorIsNil) files := map[string]string{ "/streams/v1/index.json": string(index), "/" + imagemetadata.ProductMetadataPath: string(products), } s.PatchValue(&testRoundTripper.Sub, jujutest.NewCannedRoundTripper(files, nil)) }
// ParseMetadataFromStorage loads ToolsMetadata from the specified storage reader. func ParseMetadataFromStorage(c *gc.C, stor storage.StorageReader, expectMirrors bool) []*tools.ToolsMetadata { source := storage.NewStorageSimpleStreamsDataSource("test storage reader", stor, "tools") params := simplestreams.ValueParams{ DataType: tools.ContentDownload, ValueTemplate: tools.ToolsMetadata{}, } const requireSigned = false indexPath := simplestreams.UnsignedIndex indexRef, err := simplestreams.GetIndexWithFormat( source, indexPath, "index:1.0", requireSigned, simplestreams.CloudSpec{}, params) c.Assert(err, gc.IsNil) c.Assert(indexRef.Indexes, gc.HasLen, 1) toolsIndexMetadata := indexRef.Indexes["com.ubuntu.juju:released:tools"] c.Assert(toolsIndexMetadata, gc.NotNil) // Read the products file contents. r, err := stor.Get(path.Join("tools", toolsIndexMetadata.ProductsFilePath)) defer r.Close() c.Assert(err, gc.IsNil) data, err := ioutil.ReadAll(r) c.Assert(err, gc.IsNil) url, err := source.URL(toolsIndexMetadata.ProductsFilePath) c.Assert(err, gc.IsNil) cloudMetadata, err := simplestreams.ParseCloudMetadata(data, "products:1.0", url, tools.ToolsMetadata{}) c.Assert(err, gc.IsNil) toolsMetadataMap := make(map[string]*tools.ToolsMetadata) var expectedProductIds set.Strings var toolsVersions set.Strings for _, mc := range cloudMetadata.Products { for _, items := range mc.Items { for key, item := range items.Items { toolsMetadata := item.(*tools.ToolsMetadata) toolsMetadataMap[key] = toolsMetadata toolsVersions.Add(key) seriesVersion, err := version.SeriesVersion(toolsMetadata.Release) c.Assert(err, gc.IsNil) productId := fmt.Sprintf("com.ubuntu.juju:%s:%s", seriesVersion, toolsMetadata.Arch) expectedProductIds.Add(productId) } } } // Make sure index's product IDs are all represented in the products metadata. sort.Strings(toolsIndexMetadata.ProductIds) c.Assert(toolsIndexMetadata.ProductIds, gc.DeepEquals, expectedProductIds.SortedValues()) toolsMetadata := make([]*tools.ToolsMetadata, len(toolsMetadataMap)) for i, key := range toolsVersions.SortedValues() { toolsMetadata[i] = toolsMetadataMap[key] } if expectMirrors { r, err = stor.Get(path.Join("tools", simplestreams.UnsignedMirror)) defer r.Close() c.Assert(err, gc.IsNil) data, err = ioutil.ReadAll(r) c.Assert(err, gc.IsNil) c.Assert(string(data), jc.Contains, `"mirrors":`) c.Assert(err, gc.IsNil) } return toolsMetadata }