Esempio n. 1
0
File: state.go Progetto: makyo/juju
func (st *state) loginForVersion(tag names.Tag, password, nonce string, macaroons []macaroon.Slice, vers int) error {
	var result params.LoginResultV1
	request := &params.LoginRequest{
		AuthTag:     tagToString(tag),
		Credentials: password,
		Nonce:       nonce,
		Macaroons:   macaroons,
	}
	if tag == nil {
		// Add any macaroons from the cookie jar that might work for
		// authenticating the login request.
		request.Macaroons = append(request.Macaroons,
			httpbakery.MacaroonsForURL(st.bakeryClient.Client.Jar, st.cookieURL)...,
		)
	}
	err := st.APICall("Admin", vers, "", "Login", request, &result)
	if err != nil {
		return errors.Trace(err)
	}
	if result.DischargeRequired != nil {
		// The result contains a discharge-required
		// macaroon. We discharge it and retry
		// the login request with the original macaroon
		// and its discharges.
		if result.DischargeRequiredReason == "" {
			result.DischargeRequiredReason = "no reason given for discharge requirement"
		}
		if err := st.bakeryClient.HandleError(st.cookieURL, &httpbakery.Error{
			Message: result.DischargeRequiredReason,
			Code:    httpbakery.ErrDischargeRequired,
			Info: &httpbakery.ErrorInfo{
				Macaroon:     result.DischargeRequired,
				MacaroonPath: "/",
			},
		}); err != nil {
			return errors.Trace(err)
		}
		// Add the macaroons that have been saved by HandleError to our login request.
		request.Macaroons = httpbakery.MacaroonsForURL(st.bakeryClient.Client.Jar, st.cookieURL)
		result = params.LoginResultV1{} // zero result
		err = st.APICall("Admin", vers, "", "Login", request, &result)
		if err != nil {
			return errors.Trace(err)
		}
		if result.DischargeRequired != nil {
			return errors.Errorf("login with discharged macaroons failed: %s", result.DischargeRequiredReason)
		}
	}

	servers := params.NetworkHostsPorts(result.Servers)
	err = st.setLoginResult(tag, result.ModelTag, result.ControllerTag, servers, result.Facades)
	if err != nil {
		return errors.Trace(err)
	}
	st.serverVersion, err = version.Parse(result.ServerVersion)
	if err != nil {
		return errors.Trace(err)
	}
	return nil
}
Esempio n. 2
0
File: logs.go Progetto: bac/juju
func logDocToRecord(doc *logDoc) (*LogRecord, error) {
	var ver version.Number
	if doc.Version != "" {
		parsed, err := version.Parse(doc.Version)
		if err != nil {
			return nil, errors.Annotatef(err, "invalid version %q", doc.Version)
		}
		ver = parsed
	}

	level := loggo.Level(doc.Level)
	if level > loggo.CRITICAL {
		return nil, errors.Errorf("unrecognized log level %q", doc.Level)
	}

	entity, err := names.ParseTag(doc.Entity)
	if err != nil {
		return nil, errors.Annotate(err, "while parsing entity tag")
	}

	rec := &LogRecord{
		ID:   doc.Time,
		Time: time.Unix(0, doc.Time).UTC(), // not worth preserving TZ

		ModelUUID: doc.ModelUUID,
		Entity:    entity,
		Version:   ver,

		Level:    level,
		Module:   doc.Module,
		Location: doc.Location,
		Message:  doc.Message,
	}
	return rec, nil
}
Esempio n. 3
0
// guiVersion retrieves the GUI version from the juju-gui-* directory included
// in the bz2 archive at the given path.
func guiVersion(path string) (version.Number, error) {
	var number version.Number
	f, err := os.Open(path)
	if err != nil {
		return number, errors.Annotate(err, "cannot open Juju GUI archive")
	}
	defer f.Close()
	prefix := "jujugui-"
	r := tar.NewReader(bzip2.NewReader(f))
	for {
		hdr, err := r.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return number, errors.New("cannot read Juju GUI archive")
		}
		info := hdr.FileInfo()
		if !info.IsDir() || !strings.HasPrefix(hdr.Name, prefix) {
			continue
		}
		n := info.Name()[len(prefix):]
		number, err = version.Parse(n)
		if err != nil {
			return number, errors.Errorf("cannot parse version %q", n)
		}
		return number, nil
	}
	return number, errors.New("cannot find Juju GUI version")
}
Esempio n. 4
0
// appendArchives collects all matching Juju GUI archive metadata information.
func appendArchives(
	source simplestreams.DataSource,
	matchingItems []interface{},
	items map[string]interface{},
	cons simplestreams.LookupConstraint,
) ([]interface{}, error) {
	var majorVersion int
	if guiConstraint, ok := cons.(*constraint); ok {
		majorVersion = guiConstraint.majorVersion
	}
	for _, item := range items {
		meta := item.(*Metadata)
		if majorVersion != 0 && majorVersion != meta.JujuMajorVersion {
			continue
		}
		fullPath, err := source.URL(meta.Path)
		if err != nil {
			return nil, errors.Annotate(err, "cannot retrieve metadata full path")
		}
		meta.FullPath = fullPath
		vers, err := version.Parse(meta.StringVersion)
		if err != nil {
			return nil, errors.Annotate(err, "cannot parse metadata version")
		}
		meta.Version = vers
		meta.Source = source
		matchingItems = append(matchingItems, meta)
	}
	return matchingItems, nil
}
Esempio n. 5
0
File: mgo.go Progetto: juju/testing
func detectMongoVersion() (version.Number, error) {
	mongoPath, err := getMongod()
	if err != nil {
		return version.Zero, errors.Trace(err)
	}
	output, err := exec.Command(mongoPath, "--version").Output()
	if err != nil {
		return version.Zero, errors.Trace(err)
	}
	// Read the first line of the output with a scanner (to handle
	// newlines in a cross-platform way).
	scanner := bufio.NewScanner(bytes.NewReader(output))
	versionLine := ""
	if scanner.Scan() {
		versionLine = scanner.Text()
	}
	if scanner.Err() != nil {
		return version.Zero, errors.Trace(scanner.Err())
	}
	if !strings.HasPrefix(versionLine, versionLinePrefix) {
		return version.Zero, errors.New("couldn't get mongod version - no version line")
	}
	ver, err := version.Parse(versionLine[len(versionLinePrefix):])
	if err != nil {
		return version.Zero, errors.Trace(err)
	}
	logger.Debugf("detected mongod version %v", ver)
	return ver, nil
}
Esempio n. 6
0
func (c *bootstrapCommand) Init(args []string) (err error) {
	if c.showClouds && c.showRegionsForCloud != "" {
		return errors.New("--clouds and --regions can't be used together")
	}
	if c.showClouds {
		return cmd.CheckEmpty(args)
	}
	if c.showRegionsForCloud != "" {
		return cmd.CheckEmpty(args)
	}
	if c.AgentVersionParam != "" && c.BuildAgent {
		return errors.New("--agent-version and --build-agent can't be used together")
	}
	if c.BootstrapSeries != "" && !charm.IsValidSeries(c.BootstrapSeries) {
		return errors.NotValidf("series %q", c.BootstrapSeries)
	}

	// Parse the placement directive. Bootstrap currently only
	// supports provider-specific placement directives.
	if c.Placement != "" {
		_, err = instance.ParsePlacement(c.Placement)
		if err != instance.ErrPlacementScopeMissing {
			// We only support unscoped placement directives for bootstrap.
			return errors.Errorf("unsupported bootstrap placement directive %q", c.Placement)
		}
	}
	if !c.AutoUpgrade {
		// With no auto upgrade chosen, we default to the version matching the bootstrap client.
		vers := jujuversion.Current
		c.AgentVersion = &vers
	}
	if c.AgentVersionParam != "" {
		if vers, err := version.ParseBinary(c.AgentVersionParam); err == nil {
			c.AgentVersion = &vers.Number
		} else if vers, err := version.Parse(c.AgentVersionParam); err == nil {
			c.AgentVersion = &vers
		} else {
			return err
		}
	}
	if c.AgentVersion != nil && (c.AgentVersion.Major != jujuversion.Current.Major || c.AgentVersion.Minor != jujuversion.Current.Minor) {
		return errors.New("requested agent version major.minor mismatch")
	}

	switch len(args) {
	case 0:
		// no args or flags, go interactive.
		c.interactive = true
		return nil
	}
	c.Cloud = args[0]
	if i := strings.IndexRune(c.Cloud, '/'); i > 0 {
		c.Cloud, c.Region = c.Cloud[:i], c.Cloud[i+1:]
	}
	if len(args) > 1 {
		c.controllerName = args[1]
		return cmd.CheckEmpty(args[2:])
	}
	return nil
}
Esempio n. 7
0
// archiveVersion retrieves the GUI version from the juju-gui-* directory
// included in the given tar.bz2 archive reader.
func archiveVersion(r io.Reader) (version.Number, error) {
	var vers version.Number
	prefix := "jujugui-"
	tr := tar.NewReader(bzip2.NewReader(r))
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return vers, errors.New("cannot read Juju GUI archive")
		}
		info := hdr.FileInfo()
		if !info.IsDir() || !strings.HasPrefix(hdr.Name, prefix) {
			continue
		}
		n := filepath.Dir(hdr.Name)[len(prefix):]
		vers, err = version.Parse(n)
		if err != nil {
			return vers, errors.Errorf("invalid version %q in archive", n)
		}
		return vers, nil
	}
	return vers, errors.New("cannot find Juju GUI version in archive")
}
Esempio n. 8
0
func originFromAPI(apiRec params.LogStreamRecord, controllerUUID string) (logfwd.Origin, error) {
	var origin logfwd.Origin

	tag, err := names.ParseTag(apiRec.Entity)
	if err != nil {
		return origin, errors.Annotate(err, "invalid entity")
	}

	ver, err := version.Parse(apiRec.Version)
	if err != nil {
		return origin, errors.Annotatef(err, "invalid version %q", apiRec.Version)
	}

	switch tag := tag.(type) {
	case names.MachineTag:
		origin = logfwd.OriginForMachineAgent(tag, controllerUUID, apiRec.ModelUUID, ver)
	case names.UnitTag:
		origin = logfwd.OriginForUnitAgent(tag, controllerUUID, apiRec.ModelUUID, ver)
	default:
		origin, err = logfwd.OriginForJuju(tag, controllerUUID, apiRec.ModelUUID, ver)
		if err != nil {
			return origin, errors.Annotate(err, "could not extract origin")
		}
	}
	return origin, nil
}
Esempio n. 9
0
func (c *bootstrapCommand) Init(args []string) (err error) {
	if c.AgentVersionParam != "" && c.UploadTools {
		return fmt.Errorf("--agent-version and --upload-tools can't be used together")
	}
	if c.BootstrapSeries != "" && !charm.IsValidSeries(c.BootstrapSeries) {
		return errors.NotValidf("series %q", c.BootstrapSeries)
	}
	if c.BootstrapImage != "" {
		if c.BootstrapSeries == "" {
			return errors.Errorf("--bootstrap-image must be used with --bootstrap-series")
		}
		cons, err := constraints.Merge(c.Constraints, c.BootstrapConstraints)
		if err != nil {
			return errors.Trace(err)
		}
		if !cons.HasArch() {
			return errors.Errorf("--bootstrap-image must be used with --bootstrap-constraints, specifying architecture")
		}
	}

	// Parse the placement directive. Bootstrap currently only
	// supports provider-specific placement directives.
	if c.Placement != "" {
		_, err = instance.ParsePlacement(c.Placement)
		if err != instance.ErrPlacementScopeMissing {
			// We only support unscoped placement directives for bootstrap.
			return fmt.Errorf("unsupported bootstrap placement directive %q", c.Placement)
		}
	}
	if !c.AutoUpgrade {
		// With no auto upgrade chosen, we default to the version matching the bootstrap client.
		vers := jujuversion.Current
		c.AgentVersion = &vers
	}
	if c.AgentVersionParam != "" {
		if vers, err := version.ParseBinary(c.AgentVersionParam); err == nil {
			c.AgentVersion = &vers.Number
		} else if vers, err := version.Parse(c.AgentVersionParam); err == nil {
			c.AgentVersion = &vers
		} else {
			return err
		}
	}
	if c.AgentVersion != nil && (c.AgentVersion.Major != jujuversion.Current.Major || c.AgentVersion.Minor != jujuversion.Current.Minor) {
		return fmt.Errorf("requested agent version major.minor mismatch")
	}

	// The user must specify two positional arguments: the controller name,
	// and the cloud name (optionally with region specified).
	if len(args) < 2 {
		return errors.New("controller name and cloud name are required")
	}
	c.controllerName = bootstrappedControllerName(args[0])
	c.Cloud = args[1]
	if i := strings.IndexRune(c.Cloud, '/'); i > 0 {
		c.Cloud, c.Region = c.Cloud[:i], c.Cloud[i+1:]
	}
	return cmd.CheckEmpty(args[2:])
}
Esempio n. 10
0
// AgentVersion returns the proposed version number for the agent tools,
// and whether it has been set. Once an environment is bootstrapped, this
// must always be valid.
func (c *Config) AgentVersion() (version.Number, bool) {
	if v, ok := c.defined[AgentVersionKey].(string); ok {
		n, err := version.Parse(v)
		if err != nil {
			panic(err) // We should have checked it earlier.
		}
		return n, true
	}
	return version.Zero, false
}
Esempio n. 11
0
func (*suite) TestCompare(c *gc.C) {
	cmpTests := []struct {
		v1, v2  string
		compare int
	}{
		{"1.0.0", "1.0.0", 0},
		{"01.0.0", "1.0.0", 0},
		{"10.0.0", "9.0.0", 1},
		{"1.0.0", "1.0.1", -1},
		{"1.0.1", "1.0.0", 1},
		{"1.0.0", "1.1.0", -1},
		{"1.1.0", "1.0.0", 1},
		{"1.0.0", "2.0.0", -1},
		{"1.2-alpha1", "1.2.0", -1},
		{"1.2-alpha2", "1.2-alpha1", 1},
		{"1.2-alpha2.1", "1.2-alpha2", 1},
		{"1.2-alpha2.2", "1.2-alpha2.1", 1},
		{"1.2-beta1", "1.2-alpha1", 1},
		{"1.2-beta1", "1.2-alpha2.1", 1},
		{"1.2-beta1", "1.2.0", -1},
		{"1.2.1", "1.2.0", 1},
		{"2.0.0", "1.0.0", 1},
		{"2.0.0.0", "2.0.0", 0},
		{"2.0.0.0", "2.0.0.0", 0},
		{"2.0.0.1", "2.0.0.0", 1},
		{"2.0.1.10", "2.0.0.0", 1},
	}

	for i, test := range cmpTests {
		c.Logf("test %d: %q == %q", i, test.v1, test.v2)
		v1, err := version.Parse(test.v1)
		c.Assert(err, jc.ErrorIsNil)
		v2, err := version.Parse(test.v2)
		c.Assert(err, jc.ErrorIsNil)
		compare := v1.Compare(v2)
		c.Check(compare, gc.Equals, test.compare)
		// Check that reversing the operands has
		// the expected result.
		compare = v2.Compare(v1)
		c.Check(compare, gc.Equals, -test.compare)
	}
}
Esempio n. 12
0
// binary returns the tools metadata's binary version, which may be used for
// map lookup.
func (t *ToolsMetadata) binary() (version.Binary, error) {
	num, err := version.Parse(t.Version)
	if err != nil {
		return version.Binary{}, errors.Trace(err)
	}
	return version.Binary{
		Number: num,
		Series: t.Release,
		Arch:   t.Arch,
	}, nil
}
Esempio n. 13
0
File: logsink.go Progetto: bac/juju
func jujuClientVersionFromReq(req *http.Request) (version.Number, error) {
	verStr := req.URL.Query().Get("jujuclientversion")
	if verStr == "" {
		return version.Zero, errors.New(`missing "jujuclientversion" in URL query`)
	}
	ver, err := version.Parse(verStr)
	if err != nil {
		return version.Zero, errors.Annotatef(err, "invalid jujuclientversion %q", verStr)
	}
	return ver, nil
}
Esempio n. 14
0
// 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
}
Esempio n. 15
0
func (c *ModelConfigCreator) checkVersion(base *config.Config, attrs map[string]interface{}) error {
	baseVersion, ok := base.AgentVersion()
	if !ok {
		return errors.Errorf("agent-version not found in base config")
	}

	// If there is no agent-version specified, use the current version.
	// otherwise we need to check for tools
	value, ok := attrs["agent-version"]
	if !ok {
		attrs["agent-version"] = baseVersion.String()
		return nil
	}
	versionStr, ok := value.(string)
	if !ok {
		return errors.Errorf("agent-version must be a string but has type '%T'", value)
	}
	versionNumber, err := version.Parse(versionStr)
	if err != nil {
		return errors.Trace(err)
	}

	n := versionNumber.Compare(baseVersion)
	switch {
	case n > 0:
		return errors.Errorf(
			"agent-version (%s) cannot be greater than the controller (%s)",
			versionNumber, baseVersion,
		)
	case n == 0:
		// If the version is the same as the base config,
		// then assume tools are available.
		return nil
	case n < 0:
		if c.FindTools == nil {
			return errors.New(
				"agent-version does not match base config, " +
					"and no tools-finder is supplied",
			)
		}
	}

	// Look to see if we have tools available for that version.
	list, err := c.FindTools(versionNumber)
	if err != nil {
		return errors.Trace(err)
	}
	if len(list) == 0 {
		return errors.Errorf("no tools found for version %s", versionNumber)
	}
	logger.Tracef("found tools: %#v", list)
	return nil
}
Esempio n. 16
0
func (*suite) TestParse(c *gc.C) {
	for i, test := range parseTests {
		c.Logf("test %d: %q", i, test.v)
		got, err := version.Parse(test.v)
		if test.err != "" {
			c.Assert(err, gc.ErrorMatches, test.err)
		} else {
			c.Assert(err, jc.ErrorIsNil)
			c.Assert(got, gc.Equals, test.expect)
			c.Check(got.String(), gc.Equals, test.v)
		}
	}
}
Esempio n. 17
0
// LatestToolsVersion returns the newest version found in the last
// check in the streams.
// Bear in mind that the check was performed filtering only
// new patches for the current major.minor. (major.minor.patch)
func (m *Model) LatestToolsVersion() version.Number {
	ver := m.doc.LatestAvailableTools
	if ver == "" {
		return version.Zero
	}
	v, err := version.Parse(ver)
	if err != nil {
		// This is being stored from a valid version but
		// in case this data would beacame corrupt It is not
		// worth to fail because of it.
		return version.Zero
	}
	return v
}
Esempio n. 18
0
File: gui.go Progetto: kat-co/juju
// handleGet returns information on Juju GUI archives in the controller.
func (h *guiArchiveHandler) handleGet(w http.ResponseWriter, req *http.Request) error {
	// Open the GUI archive storage.
	st, err := h.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()

	// Retrieve metadata information.
	allMeta, err := storage.AllMetadata()
	if err != nil {
		return errors.Annotate(err, "cannot retrieve GUI metadata")
	}

	// Prepare and send the response.
	var currentVersion string
	vers, err := st.GUIVersion()
	if err == nil {
		currentVersion = vers.String()
	} else if !errors.IsNotFound(err) {
		return errors.Annotate(err, "cannot retrieve current GUI version")
	}
	versions := make([]params.GUIArchiveVersion, len(allMeta))
	for i, m := range allMeta {
		vers, err := version.Parse(m.Version)
		if err != nil {
			return errors.Annotate(err, "cannot parse GUI version")
		}
		versions[i] = params.GUIArchiveVersion{
			Version: vers,
			SHA256:  m.SHA256,
			Current: m.Version == currentVersion,
		}
	}
	sendStatusAndJSON(w, http.StatusOK, params.GUIArchiveResponse{
		Versions: versions,
	})
	return nil
}
Esempio n. 19
0
func (c *upgradeJujuCommand) Init(args []string) error {
	if c.vers != "" {
		vers, err := version.Parse(c.vers)
		if err != nil {
			return err
		}
		if c.UploadTools && vers.Build != 0 {
			// TODO(fwereade): when we start taking versions from actual built
			// code, we should disable --version when used with --upload-tools.
			// For now, it's the only way to experiment with version upgrade
			// behaviour live, so the only restriction is that Build cannot
			// be used (because its value needs to be chosen internally so as
			// not to collide with existing tools).
			return fmt.Errorf("cannot specify build number when uploading tools")
		}
		c.Version = vers
	}
	return cmd.CheckEmpty(args)
}
Esempio n. 20
0
// Validate ensures that config is a valid configuration.  If old is not nil,
// it holds the previous environment configuration for consideration when
// validating changes.
func Validate(cfg, old *Config) error {
	// Check that all other fields that have been specified are non-empty,
	// unless they're allowed to be empty for backward compatibility,
	for attr, val := range cfg.defined {
		if !isEmpty(val) {
			continue
		}
		if !allowEmpty(attr) {
			return fmt.Errorf("empty %s in model configuration", attr)
		}
	}

	modelName := cfg.asString(NameKey)
	if modelName == "" {
		return errors.New("empty name in model configuration")
	}
	if !names.IsValidModelName(modelName) {
		return fmt.Errorf("%q is not a valid name: model names may only contain lowercase letters, digits and hyphens", modelName)
	}

	// Check that the agent version parses ok if set explicitly; otherwise leave
	// it alone.
	if v, ok := cfg.defined[AgentVersionKey].(string); ok {
		if _, err := version.Parse(v); err != nil {
			return fmt.Errorf("invalid agent version in model configuration: %q", v)
		}
	}

	// If the logging config is set, make sure it is valid.
	if v, ok := cfg.defined["logging-config"].(string); ok {
		if _, err := loggo.ParseConfigString(v); err != nil {
			return err
		}
	}

	if lfCfg, ok := cfg.LogFwdSyslog(); ok {
		if err := lfCfg.Validate(); err != nil {
			return errors.Annotate(err, "invalid syslog forwarding config")
		}
	}

	if uuid := cfg.UUID(); !utils.IsValidUUIDString(uuid) {
		return errors.Errorf("uuid: expected UUID, got string(%q)", uuid)
	}

	// Ensure the resource tags have the expected k=v format.
	if _, err := cfg.resourceTags(); err != nil {
		return errors.Annotate(err, "validating resource tags")
	}

	// Check the immutable config values.  These can't change
	if old != nil {
		for _, attr := range immutableAttributes {
			oldv, ok := old.defined[attr]
			if !ok {
				continue
			}
			if newv := cfg.defined[attr]; newv != oldv {
				return fmt.Errorf("cannot change %s from %#v to %#v", attr, oldv, newv)
			}
		}
		if _, oldFound := old.AgentVersion(); oldFound {
			if _, newFound := cfg.AgentVersion(); !newFound {
				return errors.New("cannot clear agent-version")
			}
		}
	}

	cfg.defined = ProcessDeprecatedAttributes(cfg.defined)
	return nil
}
Esempio n. 21
0
File: model.go Progetto: bac/juju
func importModelV1(source map[string]interface{}) (*model, error) {
	fields := schema.Fields{
		"owner":                schema.String(),
		"cloud":                schema.String(),
		"cloud-region":         schema.String(),
		"config":               schema.StringMap(schema.Any()),
		"latest-tools":         schema.String(),
		"blocks":               schema.StringMap(schema.String()),
		"users":                schema.StringMap(schema.Any()),
		"machines":             schema.StringMap(schema.Any()),
		"applications":         schema.StringMap(schema.Any()),
		"relations":            schema.StringMap(schema.Any()),
		"ssh-host-keys":        schema.StringMap(schema.Any()),
		"cloud-image-metadata": schema.StringMap(schema.Any()),
		"actions":              schema.StringMap(schema.Any()),
		"ip-addresses":         schema.StringMap(schema.Any()),
		"spaces":               schema.StringMap(schema.Any()),
		"subnets":              schema.StringMap(schema.Any()),
		"link-layer-devices":   schema.StringMap(schema.Any()),
		"volumes":              schema.StringMap(schema.Any()),
		"filesystems":          schema.StringMap(schema.Any()),
		"storages":             schema.StringMap(schema.Any()),
		"storage-pools":        schema.StringMap(schema.Any()),
		"sequences":            schema.StringMap(schema.Int()),
	}
	// Some values don't have to be there.
	defaults := schema.Defaults{
		"latest-tools": schema.Omit,
		"blocks":       schema.Omit,
		"cloud-region": schema.Omit,
	}
	addAnnotationSchema(fields, defaults)
	addConstraintsSchema(fields, defaults)
	checker := schema.FieldMap(fields, defaults)

	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, errors.Annotatef(err, "model v1 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	result := &model{
		Version:    1,
		Owner_:     valid["owner"].(string),
		Config_:    valid["config"].(map[string]interface{}),
		Sequences_: make(map[string]int),
		Blocks_:    convertToStringMap(valid["blocks"]),
		Cloud_:     valid["cloud"].(string),
	}
	result.importAnnotations(valid)
	sequences := valid["sequences"].(map[string]interface{})
	for key, value := range sequences {
		result.SetSequence(key, int(value.(int64)))
	}

	if constraintsMap, ok := valid["constraints"]; ok {
		constraints, err := importConstraints(constraintsMap.(map[string]interface{}))
		if err != nil {
			return nil, errors.Trace(err)
		}
		result.Constraints_ = constraints
	}

	if availableTools, ok := valid["latest-tools"]; ok {
		num, err := version.Parse(availableTools.(string))
		if err != nil {
			return nil, errors.Trace(err)
		}
		result.LatestToolsVersion_ = num
	}

	if region, ok := valid["cloud-region"]; ok {
		result.CloudRegion_ = region.(string)
	}

	if credential, ok := valid["cloud-credential"]; ok {
		result.CloudCredential_ = credential.(string)
	}

	userMap := valid["users"].(map[string]interface{})
	users, err := importUsers(userMap)
	if err != nil {
		return nil, errors.Annotate(err, "users")
	}
	result.setUsers(users)

	machineMap := valid["machines"].(map[string]interface{})
	machines, err := importMachines(machineMap)
	if err != nil {
		return nil, errors.Annotate(err, "machines")
	}
	result.setMachines(machines)

	applicationMap := valid["applications"].(map[string]interface{})
	applications, err := importApplications(applicationMap)
	if err != nil {
		return nil, errors.Annotate(err, "applications")
	}
	result.setApplications(applications)

	relationMap := valid["relations"].(map[string]interface{})
	relations, err := importRelations(relationMap)
	if err != nil {
		return nil, errors.Annotate(err, "relations")
	}
	result.setRelations(relations)

	spaceMap := valid["spaces"].(map[string]interface{})
	spaces, err := importSpaces(spaceMap)
	if err != nil {
		return nil, errors.Annotate(err, "spaces")
	}
	result.setSpaces(spaces)

	deviceMap := valid["link-layer-devices"].(map[string]interface{})
	devices, err := importLinkLayerDevices(deviceMap)
	if err != nil {
		return nil, errors.Annotate(err, "link-layer-devices")
	}
	result.setLinkLayerDevices(devices)

	subnetsMap := valid["subnets"].(map[string]interface{})
	subnets, err := importSubnets(subnetsMap)
	if err != nil {
		return nil, errors.Annotate(err, "subnets")
	}
	result.setSubnets(subnets)

	addressMap := valid["ip-addresses"].(map[string]interface{})
	addresses, err := importIPAddresses(addressMap)
	if err != nil {
		return nil, errors.Annotate(err, "ip-addresses")
	}
	result.setIPAddresses(addresses)

	sshHostKeyMap := valid["ssh-host-keys"].(map[string]interface{})
	hostKeys, err := importSSHHostKeys(sshHostKeyMap)
	if err != nil {
		return nil, errors.Annotate(err, "ssh-host-keys")
	}
	result.setSSHHostKeys(hostKeys)

	cloudimagemetadataMap := valid["cloud-image-metadata"].(map[string]interface{})
	cloudimagemetadata, err := importCloudImageMetadata(cloudimagemetadataMap)
	if err != nil {
		return nil, errors.Annotate(err, "cloud-image-metadata")
	}
	result.setCloudImageMetadatas(cloudimagemetadata)

	actionsMap := valid["actions"].(map[string]interface{})
	actions, err := importActions(actionsMap)
	if err != nil {
		return nil, errors.Annotate(err, "actions")
	}
	result.setActions(actions)

	volumes, err := importVolumes(valid["volumes"].(map[string]interface{}))
	if err != nil {
		return nil, errors.Annotate(err, "volumes")
	}
	result.setVolumes(volumes)

	filesystems, err := importFilesystems(valid["filesystems"].(map[string]interface{}))
	if err != nil {
		return nil, errors.Annotate(err, "filesystems")
	}
	result.setFilesystems(filesystems)

	storages, err := importStorages(valid["storages"].(map[string]interface{}))
	if err != nil {
		return nil, errors.Annotate(err, "storages")
	}
	result.setStorages(storages)

	pools, err := importStoragePools(valid["storage-pools"].(map[string]interface{}))
	if err != nil {
		return nil, errors.Annotate(err, "storage-pools")
	}
	result.setStoragePools(pools)

	return result, nil
}
Esempio n. 22
0
File: gui.go Progetto: kat-co/juju
// handlePost is used to upload new Juju GUI archives to the controller.
func (h *guiArchiveHandler) handlePost(w http.ResponseWriter, req *http.Request) error {
	// Validate the request.
	if ctype := req.Header.Get("Content-Type"); ctype != bzMimeType {
		return errors.BadRequestf("invalid content type %q: expected %q", ctype, bzMimeType)
	}
	if err := req.ParseForm(); err != nil {
		return errors.Annotate(err, "cannot parse form")
	}
	versParam := req.Form.Get("version")
	if versParam == "" {
		return errors.BadRequestf("version parameter not provided")
	}
	vers, err := version.Parse(versParam)
	if err != nil {
		return errors.BadRequestf("invalid version parameter %q", versParam)
	}
	hashParam := req.Form.Get("hash")
	if hashParam == "" {
		return errors.BadRequestf("hash parameter not provided")
	}
	if req.ContentLength == -1 {
		return errors.BadRequestf("content length not provided")
	}

	// Open the GUI archive storage.
	st, _, err := h.ctxt.stateForRequestAuthenticatedUser(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()

	// Read and validate the archive data.
	data, hash, err := readAndHash(req.Body)
	size := int64(len(data))
	if size != req.ContentLength {
		return errors.BadRequestf("archive does not match provided content length")
	}
	if hash != hashParam {
		return errors.BadRequestf("archive does not match provided hash")
	}

	// Add the archive to the GUI storage.
	metadata := binarystorage.Metadata{
		Version: vers.String(),
		Size:    size,
		SHA256:  hash,
	}
	if err := storage.Add(bytes.NewReader(data), metadata); err != nil {
		return errors.Annotate(err, "cannot add GUI archive to storage")
	}

	// Prepare and return the response.
	resp := params.GUIArchiveVersion{
		Version: vers,
		SHA256:  hash,
	}
	if currentVers, err := st.GUIVersion(); err == nil {
		if currentVers == vers {
			resp.Current = true
		}
	} else if !errors.IsNotFound(err) {
		return errors.Annotate(err, "cannot retrieve current GUI version")
	}
	sendStatusAndJSON(w, http.StatusOK, resp)
	return nil
}
Esempio n. 23
0
// openArchive opens a Juju GUI archive from the given version or file path.
// The returned readSeekCloser must be closed by callers.
func openArchive(versOrPath string) (r io.ReadCloser, hash string, size int64, vers version.Number, err error) {
	if versOrPath == "" {
		// Return the most recent Juju GUI from simplestreams.
		allMeta, err := remoteArchiveMetadata()
		if err != nil {
			return nil, "", 0, vers, errors.Annotate(err, "cannot upgrade to most recent release")
		}
		// The most recent Juju GUI release is the first on the list.
		metadata := allMeta[0]
		r, _, err := metadata.Source.Fetch(metadata.Path)
		if err != nil {
			return nil, "", 0, vers, errors.Annotatef(err, "cannot open Juju GUI archive at %q", metadata.FullPath)
		}
		return r, metadata.SHA256, metadata.Size, metadata.Version, nil
	}
	f, err := os.Open(versOrPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, "", 0, vers, errors.Annotate(err, "cannot open GUI archive")
		}
		vers, err = version.Parse(versOrPath)
		if err != nil {
			return nil, "", 0, vers, errors.Errorf("invalid GUI release version or local path %q", versOrPath)
		}
		// Return a specific release version from simplestreams.
		allMeta, err := remoteArchiveMetadata()
		if err != nil {
			return nil, "", 0, vers, errors.Annotatef(err, "cannot upgrade to release %s", vers)
		}
		metadata, err := findMetadataVersion(allMeta, vers)
		if err != nil {
			return nil, "", 0, vers, errors.Trace(err)
		}
		r, _, err := metadata.Source.Fetch(metadata.Path)
		if err != nil {
			return nil, "", 0, vers, errors.Annotatef(err, "cannot open Juju GUI archive at %q", metadata.FullPath)
		}
		return r, metadata.SHA256, metadata.Size, metadata.Version, nil
	}
	// This is a local Juju GUI release.
	defer func() {
		if err != nil {
			f.Close()
		}
	}()
	vers, err = archiveVersion(f)
	if err != nil {
		return nil, "", 0, vers, errors.Annotatef(err, "cannot upgrade Juju GUI using %q", versOrPath)
	}
	if _, err := f.Seek(0, 0); err != nil {
		return nil, "", 0, version.Number{}, errors.Annotate(err, "cannot seek archive")
	}
	hash, size, err = hashAndSize(f)
	if err != nil {
		return nil, "", 0, version.Number{}, errors.Annotatef(err, "cannot upgrade Juju GUI using %q", versOrPath)
	}
	if _, err := f.Seek(0, 0); err != nil {
		return nil, "", 0, version.Number{}, errors.Annotate(err, "cannot seek archive")
	}
	return f, hash, size, vers, nil
}
Esempio n. 24
0
func importModelV1(source map[string]interface{}) (*model, error) {
	fields := schema.Fields{
		"owner":        schema.String(),
		"config":       schema.StringMap(schema.Any()),
		"latest-tools": schema.String(),
		"blocks":       schema.StringMap(schema.String()),
		"users":        schema.StringMap(schema.Any()),
		"machines":     schema.StringMap(schema.Any()),
		"services":     schema.StringMap(schema.Any()),
		"relations":    schema.StringMap(schema.Any()),
		"sequences":    schema.StringMap(schema.Int()),
	}
	// Some values don't have to be there.
	defaults := schema.Defaults{
		"latest-tools": schema.Omit,
		"blocks":       schema.Omit,
	}
	addAnnotationSchema(fields, defaults)
	addConstraintsSchema(fields, defaults)
	checker := schema.FieldMap(fields, defaults)

	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, errors.Annotatef(err, "model v1 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	result := &model{
		Version:    1,
		Owner_:     valid["owner"].(string),
		Config_:    valid["config"].(map[string]interface{}),
		Sequences_: make(map[string]int),
		Blocks_:    convertToStringMap(valid["blocks"]),
	}
	result.importAnnotations(valid)
	sequences := valid["sequences"].(map[string]interface{})
	for key, value := range sequences {
		result.SetSequence(key, int(value.(int64)))
	}

	if constraintsMap, ok := valid["constraints"]; ok {
		constraints, err := importConstraints(constraintsMap.(map[string]interface{}))
		if err != nil {
			return nil, errors.Trace(err)
		}
		result.Constraints_ = constraints
	}

	if availableTools, ok := valid["latest-tools"]; ok {
		num, err := version.Parse(availableTools.(string))
		if err != nil {
			return nil, errors.Trace(err)
		}
		result.LatestToolsVersion_ = num
	}

	userMap := valid["users"].(map[string]interface{})
	users, err := importUsers(userMap)
	if err != nil {
		return nil, errors.Annotate(err, "users")
	}
	result.setUsers(users)

	machineMap := valid["machines"].(map[string]interface{})
	machines, err := importMachines(machineMap)
	if err != nil {
		return nil, errors.Annotate(err, "machines")
	}
	result.setMachines(machines)

	serviceMap := valid["services"].(map[string]interface{})
	services, err := importServices(serviceMap)
	if err != nil {
		return nil, errors.Annotate(err, "services")
	}
	result.setServices(services)

	relationMap := valid["relations"].(map[string]interface{})
	relations, err := importRelations(relationMap)
	if err != nil {
		return nil, errors.Annotate(err, "relations")
	}
	result.setRelations(relations)

	return result, nil
}
Esempio n. 25
0
// formatControllersTabular returns a tabular summary of controller/model items
// sorted by controller name alphabetically.
func formatControllersTabular(writer io.Writer, set ControllerSet, promptRefresh bool) error {
	tw := output.TabWriter(writer)
	w := output.Wrapper{tw}

	if promptRefresh && len(set.Controllers) > 0 {
		fmt.Fprintln(writer, "Use --refresh to see the latest information.")
		fmt.Fprintln(writer)
	}
	w.Println("CONTROLLER", "MODEL", "USER", "ACCESS", "CLOUD/REGION", "MODELS", "MACHINES", "HA", "VERSION")
	tw.SetColumnAlignRight(5)
	tw.SetColumnAlignRight(6)
	tw.SetColumnAlignRight(7)

	names := []string{}
	for name := range set.Controllers {
		names = append(names, name)
	}
	sort.Strings(names)

	for _, name := range names {
		c := set.Controllers[name]
		modelName := noValueDisplay
		if c.ModelName != "" {
			modelName = c.ModelName
		}
		userName := noValueDisplay
		access := noValueDisplay
		if c.User != "" {
			userName = c.User
			access = notKnownDisplay
			if c.Access != "" {
				access = c.Access
			}
		}
		if name == set.CurrentController {
			name += "*"
			w.PrintColor(output.CurrentHighlight, name)
		} else {
			w.Print(name)
		}
		cloudRegion := c.Cloud
		if c.CloudRegion != "" {
			cloudRegion += "/" + c.CloudRegion
		}
		agentVersion := c.AgentVersion
		staleVersion := false
		if agentVersion == "" {
			agentVersion = notKnownDisplay
		} else {
			agentVersionNum, err := version.Parse(agentVersion)
			staleVersion = err == nil && jujuversion.Current.Compare(agentVersionNum) > 0
		}
		machineCount := noValueDisplay
		if c.MachineCount != nil && *c.MachineCount > 0 {
			machineCount = fmt.Sprintf("%d", *c.MachineCount)
		}
		modelCount := noValueDisplay
		if c.ModelCount != nil && *c.ModelCount > 0 {
			modelCount = fmt.Sprintf("%d", *c.ModelCount)
		}
		w.Print(modelName, userName, access, cloudRegion, modelCount, machineCount)
		controllerMachineInfo, warn := controllerMachineStatus(c.ControllerMachines)
		if warn {
			w.PrintColor(output.WarningHighlight, controllerMachineInfo)
		} else {
			w.Print(controllerMachineInfo)
		}
		if staleVersion {
			w.PrintColor(output.WarningHighlight, agentVersion)
		} else {
			w.Print(agentVersion)
		}
		w.Println()
	}
	tw.Flush()
	return nil
}