// PrepareHook is part of the operation.Callbacks interface. func (opc *operationCallbacks) PrepareHook(hi hook.Info) (string, error) { name := string(hi.Kind) switch { case hi.Kind.IsRelation(): var err error name, err = opc.u.relations.PrepareHook(hi) if err != nil { return "", err } case hi.Kind.IsStorage(): if err := opc.u.storage.ValidateHook(hi); err != nil { return "", err } storageName, err := names.StorageName(hi.StorageId) if err != nil { return "", err } name = fmt.Sprintf("%s-%s", storageName, hi.Kind) // TODO(axw) if the agent is not installed yet, // set the status to "preparing storage". case hi.Kind == hooks.ConfigChanged: // TODO(axw) //opc.u.f.DiscardConfigEvent() case hi.Kind == hook.LeaderSettingsChanged: // TODO(axw) //opc.u.f.DiscardLeaderSettingsEvent() } return name, nil }
// HookContext is part of the ContextFactory interface. func (f *contextFactory) HookContext(hookInfo hook.Info) (*HookContext, error) { ctx, err := f.coreContext() if err != nil { return nil, errors.Trace(err) } hookName := string(hookInfo.Kind) if hookInfo.Kind.IsRelation() { ctx.relationId = hookInfo.RelationId ctx.remoteUnitName = hookInfo.RemoteUnit relation, found := ctx.relations[hookInfo.RelationId] if !found { return nil, errors.Errorf("unknown relation id: %v", hookInfo.RelationId) } if hookInfo.Kind == hooks.RelationDeparted { relation.cache.RemoveMember(hookInfo.RemoteUnit) } else if hookInfo.RemoteUnit != "" { // Clear remote settings cache for changing remote unit. relation.cache.InvalidateMember(hookInfo.RemoteUnit) } hookName = fmt.Sprintf("%s-%s", relation.Name(), hookInfo.Kind) } if hookInfo.Kind.IsStorage() { ctx.storageTag = names.NewStorageTag(hookInfo.StorageId) if _, err := ctx.storage.Storage(ctx.storageTag); err != nil { return nil, errors.Annotatef(err, "could not retrieve storage for id: %v", hookInfo.StorageId) } storageName, err := names.StorageName(hookInfo.StorageId) if err != nil { return nil, errors.Trace(err) } hookName = fmt.Sprintf("%s-%s", storageName, hookName) } ctx.id = f.newId(hookName) return ctx, nil }
// HookContext is part of the ContextFactory interface. func (f *contextFactory) HookContext(hookInfo hook.Info) (*HookContext, error) { ctx, err := f.coreContext() if err != nil { return nil, errors.Trace(err) } hookName := string(hookInfo.Kind) if hookInfo.Kind.IsRelation() { ctx.relationId = hookInfo.RelationId ctx.remoteUnitName = hookInfo.RemoteUnit relation, found := ctx.relations[hookInfo.RelationId] if !found { return nil, errors.Errorf("unknown relation id: %v", hookInfo.RelationId) } if hookInfo.Kind == hooks.RelationDeparted { relation.cache.RemoveMember(hookInfo.RemoteUnit) } else if hookInfo.RemoteUnit != "" { // Clear remote settings cache for changing remote unit. relation.cache.InvalidateMember(hookInfo.RemoteUnit) } hookName = fmt.Sprintf("%s-%s", relation.Name(), hookInfo.Kind) } if hookInfo.Kind.IsStorage() { ctx.storageTag = names.NewStorageTag(hookInfo.StorageId) if _, found := ctx.storage.Storage(ctx.storageTag); !found { return nil, errors.Errorf("unknown storage id: %v", hookInfo.StorageId) } storageName, err := names.StorageName(hookInfo.StorageId) if err != nil { return nil, errors.Trace(err) } hookName = fmt.Sprintf("%s-%s", storageName, hookName) } // Metrics are only sent from the collect-metrics hook. if hookInfo.Kind == hooks.CollectMetrics { ch, err := getCharm(f.paths.GetCharmDir()) if err != nil { return nil, errors.Trace(err) } ctx.definedMetrics = ch.Metrics() chURL, err := f.unit.CharmURL() if err != nil { return nil, errors.Trace(err) } charmMetrics := map[string]charm.Metric{} if ch.Metrics() != nil { charmMetrics = ch.Metrics().Metrics } ctx.metricsRecorder, err = metrics.NewJSONMetricRecorder( f.paths.GetMetricsSpoolDir(), charmMetrics, chURL.String()) if err != nil { return nil, errors.Trace(err) } } ctx.id = f.newId(hookName) return ctx, nil }
func (c *StorageListCommand) Run(ctx *cmd.Context) error { tags, err := c.ctx.StorageTags() if err != nil { return errors.Trace(err) } ids := make([]string, 0, len(tags)) for _, tag := range tags { id := tag.Id() if c.storageName != "" { storageName, err := names.StorageName(id) if err != nil { return errors.Trace(err) } if storageName != c.storageName { continue } } ids = append(ids, id) } return c.out.Write(ctx, ids) }
// formatStorageDetails takes a set of StorageDetail and creates a // mapping keyed on unit and storage id. func formatStorageDetails(storages []params.StorageDetails) (map[string]map[string]StorageInfo, error) { if len(storages) == 0 { return nil, nil } output := make(map[string]map[string]StorageInfo) for _, one := range storages { storageTag, err := names.ParseStorageTag(one.StorageTag) if err != nil { return nil, errors.Annotate(err, "invalid storage tag") } unitTag, err := names.ParseTag(one.UnitTag) if err != nil { return nil, errors.Annotate(err, "invalid unit tag") } storageName, err := names.StorageName(storageTag.Id()) if err != nil { panic(err) // impossible } si := StorageInfo{ StorageName: storageName, Kind: one.Kind.String(), Status: one.Status, Location: one.Location, Persistent: one.Persistent, } unit := unitTag.Id() unitColl, ok := output[unit] if !ok { unitColl = map[string]StorageInfo{} output[unit] = unitColl } unitColl[storageTag.Id()] = si } return output, nil }
// validateFilesystemMountPoints validates the mount points of filesystems // being attached to the specified machine. If there are any mount point // path conflicts, an error will be returned. func validateFilesystemMountPoints(m *Machine, newFilesystems []filesystemAttachmentTemplate) error { attachments, err := m.st.MachineFilesystemAttachments(m.MachineTag()) if err != nil { return errors.Trace(err) } existing := make(map[names.FilesystemTag]string) for _, a := range attachments { params, ok := a.Params() if ok { existing[a.Filesystem()] = params.Location continue } info, err := a.Info() if err != nil { return errors.Trace(err) } existing[a.Filesystem()] = info.MountPoint } storageName := func( filesystemTag names.FilesystemTag, storageTag names.StorageTag, ) string { if storageTag == (names.StorageTag{}) { return names.ReadableString(filesystemTag) } // We know the tag is valid, so ignore the error. storageName, _ := names.StorageName(storageTag.Id()) return fmt.Sprintf("%q storage", storageName) } containsPath := func(a, b string) bool { a = path.Clean(a) + "/" b = path.Clean(b) + "/" return strings.HasPrefix(b, a) } // These sets are expected to be small, so sorting and comparing // adjacent values is not worth the cost of creating a reverse // lookup from location to filesystem. for _, template := range newFilesystems { newMountPoint := template.params.Location for oldFilesystemTag, oldMountPoint := range existing { var conflicted, swapOrder bool if containsPath(oldMountPoint, newMountPoint) { conflicted = true } else if containsPath(newMountPoint, oldMountPoint) { conflicted = true swapOrder = true } if !conflicted { continue } // Get a helpful identifier for the new filesystem. If it // is being created for a storage instance, then use // the storage name; otherwise use the filesystem name. newStorageName := storageName(template.tag, template.storage) // Likewise for the old filesystem, but this time we'll // need to consult state. oldFilesystem, err := m.st.Filesystem(oldFilesystemTag) if err != nil { return errors.Trace(err) } storageTag, err := oldFilesystem.Storage() if errors.IsNotAssigned(err) { storageTag = names.StorageTag{} } else if err != nil { return errors.Trace(err) } oldStorageName := storageName(oldFilesystemTag, storageTag) lhs := fmt.Sprintf("mount point %q for %s", oldMountPoint, oldStorageName) rhs := fmt.Sprintf("mount point %q for %s", newMountPoint, newStorageName) if swapOrder { lhs, rhs = rhs, lhs } return errors.Errorf("%s contains %s", lhs, rhs) } } return nil }
func assertStorageNameInvalid(c *gc.C, id string) { _, err := names.StorageName(id) expect := fmt.Sprintf("%q is not a valid storage instance ID", id) c.Assert(err, gc.ErrorMatches, expect) }
func assertStorageNameValid(c *gc.C, id, expect string) { name, err := names.StorageName(id) c.Assert(err, gc.IsNil) c.Assert(name, gc.Equals, expect) }