func (s *BootstrapSuite) TestGUIArchiveInfoError(c *gc.C) { if runtime.GOOS == "windows" { // TODO frankban: skipping for now due to chmod problems with mode 0000 // on Windows. We will re-enable this test after further investigation: // "jujud bootstrap" is never run on Windows anyway. c.Skip("needs chmod investigation") } dir := filepath.FromSlash(agenttools.SharedGUIDir(s.dataDir)) info := filepath.Join(dir, "downloaded-gui.txt") err := os.Chmod(info, 0000) c.Assert(err, jc.ErrorIsNil) defer os.Chmod(info, 0600) _, cmd, err := s.initBootstrapCommand( c, nil, "--model-config", s.b64yamlControllerModelConfig, "--hosted-model-config", s.b64yamlHostedModelConfig, "--instance-id", string(s.instanceId)) c.Assert(err, jc.ErrorIsNil) var tw loggo.TestWriter err = loggo.RegisterWriter("bootstrap-test", &tw, loggo.DEBUG) c.Assert(err, jc.ErrorIsNil) defer loggo.RemoveWriter("bootstrap-test") err = cmd.Run(nil) c.Assert(err, jc.ErrorIsNil) c.Assert(tw.Log(), jc.LogMatches, jc.SimpleMessages{{ loggo.WARNING, `cannot set up Juju GUI: cannot fetch GUI info: cannot read GUI metadata in tools directory: .*`, }}) }
func (s *guiSuite) TestGUIDirectory(c *gc.C) { storage, err := s.State.GUIStorage() c.Assert(err, jc.ErrorIsNil) defer storage.Close() // Create a Juju GUI archive and save it into the storage. indexContent := "<!DOCTYPE html><html><body>Exterminate!</body></html>" vers := version.MustParse("2.0.0") hash := setupGUIArchive(c, storage, vers.String(), map[string]string{ guiIndexPath: indexContent, apiserver.SpritePath: "", }) err = s.State.GUISetVersion(vers) c.Assert(err, jc.ErrorIsNil) // Initially the GUI directory on the server is empty. baseDir := agenttools.SharedGUIDir(s.DataDir()) c.Assert(baseDir, jc.DoesNotExist) // Make a request for the Juju GUI. resp := s.sendRequest(c, httpRequestParams{ url: s.guiURL(c, "", "/"), }) body := assertResponse(c, resp, http.StatusOK, "text/html; charset=utf-8") c.Assert(string(body), gc.Equals, indexContent) // Now the GUI is stored on disk, in a directory corresponding to its // archive SHA256 hash. indexPath := filepath.Join(baseDir, hash, guiIndexPath) c.Assert(indexPath, jc.IsNonEmptyFile) b, err := ioutil.ReadFile(indexPath) c.Assert(err, jc.ErrorIsNil) c.Assert(string(b), gc.Equals, indexContent) }
// populateGUIArchive stores the uploaded Juju GUI archive in provider storage, // updates the GUI metadata and set the current Juju GUI version. func (c *BootstrapCommand) populateGUIArchive(st *state.State, env environs.Environ) error { agentConfig := c.CurrentConfig() dataDir := agentConfig.DataDir() guistorage, err := st.GUIStorage() if err != nil { return errors.Trace(err) } defer guistorage.Close() gui, err := agenttools.ReadGUIArchive(dataDir) if err != nil { return errors.Annotate(err, "cannot fetch GUI info") } f, err := os.Open(filepath.Join(agenttools.SharedGUIDir(dataDir), "gui.tar.bz2")) if err != nil { return errors.Annotate(err, "cannot read GUI archive") } defer f.Close() if err := guistorage.Add(f, binarystorage.Metadata{ Version: gui.Version.String(), Size: gui.Size, SHA256: gui.SHA256, }); err != nil { return errors.Annotate(err, "cannot store GUI archive") } if err = st.GUISetVersion(gui.Version); err != nil { return errors.Annotate(err, "cannot set current GUI version") } return nil }
func (s *BootstrapSuite) writeDownloadedGUI(c *gc.C, gui *tools.GUIArchive) { guiDir := filepath.FromSlash(agenttools.SharedGUIDir(s.dataDir)) err := os.MkdirAll(guiDir, 0755) c.Assert(err, jc.ErrorIsNil) data, err := json.Marshal(gui) c.Assert(err, jc.ErrorIsNil) err = ioutil.WriteFile(filepath.Join(guiDir, "downloaded-gui.txt"), data, 0644) c.Assert(err, jc.ErrorIsNil) }
func (t *ToolsSuite) TestReadGUIArchiveErrorNotValid(c *gc.C) { dir := agenttools.SharedGUIDir(t.dataDir) err := os.MkdirAll(dir, agenttools.DirPerm) c.Assert(err, jc.ErrorIsNil) err = ioutil.WriteFile(filepath.Join(dir, agenttools.GUIArchiveFile), []byte(" \t\n"), 0644) c.Assert(err, jc.ErrorIsNil) gui, err := agenttools.ReadGUIArchive(t.dataDir) c.Assert(err, gc.ErrorMatches, "invalid GUI metadata in tools directory .*") c.Assert(gui, gc.IsNil) }
// ensureFiles checks that the GUI files are available on disk. // If they are not, it means this is the first time this Juju GUI version is // accessed. In this case, retrieve the Juju GUI archive from the storage and // uncompress it to disk. This function returns the current GUI root directory // and archive hash. func (gr *guiRouter) ensureFiles(req *http.Request) (rootDir string, hash string, err error) { // Retrieve the Juju GUI info from the GUI storage. st, err := gr.ctxt.stateForRequestUnauthenticated(req) if err != nil { return "", "", errors.Annotate(err, "cannot open state") } storage, err := st.GUIStorage() if err != nil { return "", "", errors.Annotate(err, "cannot open GUI storage") } defer storage.Close() vers, hash, err := guiVersionAndHash(st, storage) if err != nil { return "", "", errors.Trace(err) } logger.Debugf("serving Juju GUI version %s", vers) // Check if the current Juju GUI archive has been already expanded on disk. baseDir := agenttools.SharedGUIDir(gr.dataDir) // Note that we include the hash in the root directory so that when the GUI // archive changes we can be sure that clients will not use files from // mixed versions. rootDir = filepath.Join(baseDir, hash) info, err := os.Stat(rootDir) if err == nil { if info.IsDir() { return rootDir, hash, nil } return "", "", errors.Errorf("cannot use Juju GUI root directory %q: not a directory", rootDir) } if !os.IsNotExist(err) { return "", "", errors.Annotate(err, "cannot stat Juju GUI root directory") } // Fetch the Juju GUI archive from the GUI storage and expand it. _, r, err := storage.Open(vers) if err != nil { return "", "", errors.Annotatef(err, "cannot find GUI archive version %q", vers) } defer r.Close() if err := os.MkdirAll(baseDir, 0755); err != nil { return "", "", errors.Annotate(err, "cannot create Juju GUI base directory") } guiDir := "jujugui-" + vers + "/jujugui" if err := uncompressGUI(r, guiDir, rootDir); err != nil { return "", "", errors.Annotate(err, "cannot uncompress Juju GUI archive") } return rootDir, hash, nil }
func (t *ToolsSuite) TestReadGUIArchiveSuccess(c *gc.C) { dir := agenttools.SharedGUIDir(t.dataDir) err := os.MkdirAll(dir, agenttools.DirPerm) c.Assert(err, jc.ErrorIsNil) expectGUI := coretest.GUIArchive{ Version: version.MustParse("2.0.42"), URL: "file:///path/to/gui", SHA256: "hash", Size: 47, } b, err := json.Marshal(expectGUI) c.Assert(err, jc.ErrorIsNil) err = ioutil.WriteFile(filepath.Join(dir, agenttools.GUIArchiveFile), b, 0644) c.Assert(err, jc.ErrorIsNil) gui, err := agenttools.ReadGUIArchive(t.dataDir) c.Assert(err, jc.ErrorIsNil) c.Assert(*gui, gc.Equals, expectGUI) }
func (s *BootstrapSuite) TestGUIArchiveError(c *gc.C) { dir := filepath.FromSlash(agenttools.SharedGUIDir(s.dataDir)) archive := filepath.Join(dir, "gui.tar.bz2") err := os.Remove(archive) c.Assert(err, jc.ErrorIsNil) _, cmd, err := s.initBootstrapCommand( c, nil, "--model-config", s.b64yamlControllerModelConfig, "--hosted-model-config", s.b64yamlHostedModelConfig, "--instance-id", string(s.instanceId)) c.Assert(err, jc.ErrorIsNil) var tw loggo.TestWriter err = loggo.RegisterWriter("bootstrap-test", &tw, loggo.DEBUG) c.Assert(err, jc.ErrorIsNil) defer loggo.RemoveWriter("bootstrap-test") err = cmd.Run(nil) c.Assert(tw.Log(), jc.LogMatches, jc.SimpleMessages{{ loggo.WARNING, `cannot set up Juju GUI: cannot read GUI archive: .*`, }}) }
func (s *BootstrapSuite) SetUpTest(c *gc.C) { s.BaseSuite.SetUpTest(c) s.PatchValue(&sshGenerateKey, func(name string) (string, string, error) { return "private-key", "public-key", nil }) s.MgoSuite.SetUpTest(c) s.dataDir = c.MkDir() s.logDir = c.MkDir() s.mongoOplogSize = "1234" s.fakeEnsureMongo = agenttesting.InstallFakeEnsureMongo(s) s.PatchValue(&initiateMongoServer, s.fakeEnsureMongo.InitiateMongo) // Create fake tools.tar.gz and downloaded-tools.txt. current := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } toolsDir := filepath.FromSlash(agenttools.SharedToolsDir(s.dataDir, current)) err := os.MkdirAll(toolsDir, 0755) c.Assert(err, jc.ErrorIsNil) err = ioutil.WriteFile(filepath.Join(toolsDir, "tools.tar.gz"), nil, 0644) c.Assert(err, jc.ErrorIsNil) s.writeDownloadedTools(c, &tools.Tools{Version: current}) // Create fake gui.tar.bz2 and downloaded-gui.txt. guiDir := filepath.FromSlash(agenttools.SharedGUIDir(s.dataDir)) err = os.MkdirAll(guiDir, 0755) c.Assert(err, jc.ErrorIsNil) err = ioutil.WriteFile(filepath.Join(guiDir, "gui.tar.bz2"), nil, 0644) c.Assert(err, jc.ErrorIsNil) s.writeDownloadedGUI(c, &tools.GUIArchive{ Version: version.MustParse("2.0.42"), }) }
// GUITools returns the directory where the Juju GUI release is stored. func (cfg *InstanceConfig) GUITools() string { return agenttools.SharedGUIDir(cfg.DataDir) }
func (s *guiSuite) TestGUIHandler(c *gc.C) { if runtime.GOOS == "windows" { // Skipping the tests on Windows is not a problem as the Juju GUI is // only served from Linux machines. c.Skip("bzip2 command not available") } sendRequest := func(setup guiSetupFunc, currentVersion, pathAndquery string) *http.Response { // Set up the GUI base directory. datadir := filepath.ToSlash(s.DataDir()) baseDir := filepath.FromSlash(agenttools.SharedGUIDir(datadir)) defer func() { os.Chmod(baseDir, 0755) os.Remove(baseDir) }() // Run specific test set up. var hash string if setup != nil { storage, err := s.State.GUIStorage() c.Assert(err, jc.ErrorIsNil) defer storage.Close() // Ensure the GUI storage is empty. allMeta, err := storage.AllMetadata() c.Assert(err, jc.ErrorIsNil) c.Assert(allMeta, gc.HasLen, 0) hash = setup(c, baseDir, storage) } // Set the current GUI version if required. if currentVersion != "" { err := s.State.GUISetVersion(version.MustParse(currentVersion)) c.Assert(err, jc.ErrorIsNil) } // Send a request to the test path. if pathAndquery == "" { pathAndquery = "/" } return s.sendRequest(c, httpRequestParams{ url: s.guiURL(c, hash, pathAndquery), }) } for i, test := range guiHandlerTests { c.Logf("\n%d: %s", i, test.about) // Reset the db so that the GUI storage is empty in each test. s.Reset(c) // Perform the request. resp := sendRequest(test.setup, test.currentVersion, test.pathAndquery) // Check the response. if test.expectedStatus == 0 { test.expectedStatus = http.StatusOK } if test.expectedError != "" { test.expectedContentType = params.ContentTypeJSON } body := assertResponse(c, resp, test.expectedStatus, test.expectedContentType) if test.expectedError == "" { c.Check(string(body), gc.Equals, test.expectedBody) } else { var jsonResp params.ErrorResult err := json.Unmarshal(body, &jsonResp) if !c.Check(err, jc.ErrorIsNil, gc.Commentf("body: %s", body)) { continue } c.Check(jsonResp.Error.Message, gc.Matches, test.expectedError) } } }
func (t *ToolsSuite) TestSharedGUIDir(c *gc.C) { dir := agenttools.SharedGUIDir("/var/lib/juju") c.Assert(dir, gc.Equals, "/var/lib/juju/gui") }