func (s *UpgradeCharmSuccessStateSuite) TestForcedSeriesUpgrade(c *gc.C) { path := testcharms.Repo.ClonedDirPath(c.MkDir(), "multi-series") err := runDeploy(c, path, "multi-series", "--series", "precise") c.Assert(err, jc.ErrorIsNil) application, err := s.State.Application("multi-series") c.Assert(err, jc.ErrorIsNil) ch, _, err := application.Charm() c.Assert(err, jc.ErrorIsNil) c.Assert(ch.Revision(), gc.Equals, 1) // Copy files from a charm supporting a different set of series // so we can try an upgrade requiring --force-series. for _, f := range []string{"metadata.yaml", "revision"} { err = utils.CopyFile( filepath.Join(path, f), filepath.Join(testcharms.Repo.CharmDirPath("multi-series2"), f)) c.Assert(err, jc.ErrorIsNil) } err = runUpgradeCharm(c, "multi-series", "--path", path, "--force-series") c.Assert(err, jc.ErrorIsNil) err = application.Refresh() c.Assert(err, jc.ErrorIsNil) ch, force, err := application.Charm() c.Assert(err, jc.ErrorIsNil) c.Assert(ch.Revision(), gc.Equals, 8) c.Assert(force, gc.Equals, false) s.AssertCharmUploaded(c, ch.URL()) c.Assert(ch.URL().String(), gc.Equals, "local:precise/multi-series2-8") }
// Clone creates a copy of the container, giving the copy the specified name. func (mock *mockContainer) Clone(name string, extraArgs []string, templateArgs []string) (golxc.Container, error) { state := mock.getState() if state == golxc.StateUnknown { return nil, fmt.Errorf("container has not been created") } else if state == golxc.StateRunning { return nil, fmt.Errorf("container is running, clone not possible") } container := &mockContainer{ factory: mock.factory, name: name, state: golxc.StateStopped, logLevel: golxc.LogWarning, } mock.factory.instances[name] = container // Create the container directory. containerDir := filepath.Join(mock.factory.containerDir, name) if err := os.MkdirAll(containerDir, 0755); err != nil { return nil, errors.Trace(err) } if err := utils.CopyFile(container.configFilename(), mock.configFilename()); err != nil { return nil, errors.Trace(err) } mock.factory.notify(eventArgs(Cloned, mock.name, extraArgs, templateArgs, nil)) return container, nil }
// populateTools stores uploaded tools in provider storage // and updates the tools metadata. // // TODO(axw) store tools in gridfs, catalogue in state. func (c *BootstrapCommand) populateTools(env environs.Environ) error { agentConfig := c.CurrentConfig() dataDir := agentConfig.DataDir() tools, err := agenttools.ReadTools(dataDir, version.Current) if err != nil { return err } if !strings.HasPrefix(tools.URL, "file://") { // Nothing to do since the tools were not uploaded. return nil } // This is a hack: providers using localstorage (local, manual) // can't use storage during bootstrap as the localstorage worker // isn't running. Use filestorage instead. var stor storage.Storage storageDir := agentConfig.Value(agent.StorageDir) if storageDir != "" { stor, err = filestorage.NewFileStorageWriter(storageDir) if err != nil { return err } } else { stor = env.Storage() } // Create a temporary directory to contain source and cloned tools. tempDir, err := ioutil.TempDir("", "juju-sync-tools") if err != nil { return err } defer os.RemoveAll(tempDir) destTools := filepath.Join(tempDir, filepath.FromSlash(envtools.StorageName(tools.Version))) if err := os.MkdirAll(filepath.Dir(destTools), 0700); err != nil { return err } srcTools := filepath.Join( agenttools.SharedToolsDir(dataDir, version.Current), "tools.tar.gz", ) if err := utils.CopyFile(destTools, srcTools); err != nil { return err } // Until we catalogue tools in state, we clone the tools // for each of the supported series of the same OS. otherSeries := version.OSSupportedSeries(version.Current.OS) _, err = sync.SyncBuiltTools(stor, &sync.BuiltTools{ Version: tools.Version, Dir: tempDir, StorageName: envtools.StorageName(tools.Version), Sha256Hash: tools.SHA256, Size: tools.Size, }, otherSeries...) return err }
// buildToolsTarball bundles a tools tarball and places it in a temp directory in // the expected tools path. func buildToolsTarball(forceVersion *version.Number) (builtTools *BuiltTools, err error) { // TODO(rog) find binaries from $PATH when not using a development // version of juju within a $GOPATH. logger.Debugf("Building tools") // We create the entire archive before asking the environment to // start uploading so that we can be sure we have archived // correctly. f, err := ioutil.TempFile("", "juju-tgz") if err != nil { return nil, err } defer f.Close() defer os.Remove(f.Name()) toolsVersion, sha256Hash, err := envtools.BundleTools(f, forceVersion) if err != nil { return nil, err } fileInfo, err := f.Stat() if err != nil { return nil, fmt.Errorf("cannot stat newly made tools archive: %v", err) } size := fileInfo.Size() logger.Infof("built tools %v (%dkB)", toolsVersion, (size+512)/1024) baseToolsDir, err := ioutil.TempDir("", "juju-tools") if err != nil { return nil, err } // If we exit with an error, clean up the built tools directory. defer func() { if err != nil { os.RemoveAll(baseToolsDir) } }() err = os.MkdirAll(filepath.Join(baseToolsDir, storage.BaseToolsPath, "releases"), 0755) if err != nil { return nil, err } storageName := envtools.StorageName(toolsVersion) err = utils.CopyFile(filepath.Join(baseToolsDir, storageName), f.Name()) if err != nil { return nil, err } return &BuiltTools{ Version: toolsVersion, Dir: baseToolsDir, StorageName: storageName, Size: size, Sha256Hash: sha256Hash, }, nil }
// 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 := jujuseries.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) }
func (*fileSuite) TestCopyFile(c *gc.C) { dir := c.MkDir() f, err := ioutil.TempFile(dir, "source") c.Assert(err, gc.IsNil) defer f.Close() _, err = f.Write([]byte("hello world")) c.Assert(err, gc.IsNil) dest := filepath.Join(dir, "dest") err = utils.CopyFile(dest, f.Name()) c.Assert(err, gc.IsNil) data, err := ioutil.ReadFile(dest) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "hello world") }
// Create creates a new container based on the given template. func (mock *mockContainer) Create(configFile, template string, extraArgs []string, templateArgs []string, envArgs []string) error { if mock.getState() != golxc.StateUnknown { return fmt.Errorf("container is already created") } mock.factory.instances[mock.name] = mock // Create the container directory. containerDir := filepath.Join(mock.factory.containerDir, mock.name) if err := os.MkdirAll(containerDir, 0755); err != nil { return errors.Trace(err) } if err := utils.CopyFile(mock.configFilename(), configFile); err != nil { return errors.Trace(err) } mock.setState(golxc.StateStopped) mock.factory.notify(eventArgs(Created, mock.name, extraArgs, templateArgs, envArgs)) return nil }
// BuildAgentTarball bundles an agent tarball and places it in a temp directory in // the expected agent path. func buildAgentTarball(build bool, forceVersion *version.Number, stream string) (_ *BuiltAgent, err error) { // TODO(rog) find binaries from $PATH when not using a development // version of juju within a $GOPATH. logger.Debugf("Making agent binary tarball") // We create the entire archive before asking the environment to // start uploading so that we can be sure we have archived // correctly. f, err := ioutil.TempFile("", "juju-tgz") if err != nil { return nil, err } defer f.Close() defer os.Remove(f.Name()) toolsVersion, sha256Hash, err := envtools.BundleTools(build, f, forceVersion) if err != nil { return nil, err } // Built agent version needs to match the client used to bootstrap. builtVersion := toolsVersion builtVersion.Build = 0 clientVersion := jujuversion.Current clientVersion.Build = 0 if builtVersion.Number.Compare(clientVersion) != 0 { return nil, errors.Errorf("agent binary %v not compatibile with bootstrap client %v", toolsVersion.Number, jujuversion.Current) } fileInfo, err := f.Stat() if err != nil { return nil, errors.Errorf("cannot stat newly made tools archive: %v", err) } size := fileInfo.Size() reportedVersion := toolsVersion if forceVersion != nil { reportedVersion.Number = *forceVersion } logger.Infof("using agent binary %v aliased to %v (%dkB)", toolsVersion, reportedVersion, (size+512)/1024) baseToolsDir, err := ioutil.TempDir("", "juju-tools") if err != nil { return nil, err } // If we exit with an error, clean up the built tools directory. defer func() { if err != nil { os.RemoveAll(baseToolsDir) } }() err = os.MkdirAll(filepath.Join(baseToolsDir, storage.BaseToolsPath, stream), 0755) if err != nil { return nil, err } storageName := envtools.StorageName(toolsVersion, stream) err = utils.CopyFile(filepath.Join(baseToolsDir, storageName), f.Name()) if err != nil { return nil, err } return &BuiltAgent{ Version: toolsVersion, Dir: baseToolsDir, StorageName: storageName, Size: size, Sha256Hash: sha256Hash, }, nil }