func (st *state) loginForVersion(tag names.Tag, password, nonce string, macaroons []macaroon.Slice, vers int) error { var result params.LoginResultV1 request := ¶ms.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 }
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 }
// 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") }
// 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 }
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 }
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 }
// 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") }
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 }
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:]) }
// 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 }
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) } }
// 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 }
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 }
// 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 (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 }
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) } } }
// 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 }
// 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 }
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) }
// 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 }
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 }
// 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 }
// 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 }
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 }
// 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 }