docker/swarmkit

View on GitHub
manager/state/store/extensions.go

Summary

Maintainability
A
35 mins
Test Coverage
package store

import (
    "errors"
    "strings"

    memdb "github.com/hashicorp/go-memdb"
    "github.com/moby/swarmkit/v2/api"
)

const tableExtension = "extension"

func init() {
    register(ObjectStoreConfig{
        Table: &memdb.TableSchema{
            Name: tableExtension,
            Indexes: map[string]*memdb.IndexSchema{
                indexID: {
                    Name:    indexID,
                    Unique:  true,
                    Indexer: extensionIndexerByID{},
                },
                indexName: {
                    Name:    indexName,
                    Unique:  true,
                    Indexer: extensionIndexerByName{},
                },
                indexCustom: {
                    Name:         indexCustom,
                    Indexer:      extensionCustomIndexer{},
                    AllowMissing: true,
                },
            },
        },
        Save: func(tx ReadTx, snapshot *api.StoreSnapshot) error {
            var err error
            snapshot.Extensions, err = FindExtensions(tx, All)
            return err
        },
        Restore: func(tx Tx, snapshot *api.StoreSnapshot) error {
            toStoreObj := make([]api.StoreObject, len(snapshot.Extensions))
            for i, x := range snapshot.Extensions {
                toStoreObj[i] = extensionEntry{x}
            }
            return RestoreTable(tx, tableExtension, toStoreObj)
        },
        ApplyStoreAction: func(tx Tx, sa api.StoreAction) error {
            switch v := sa.Target.(type) {
            case *api.StoreAction_Extension:
                obj := v.Extension
                switch sa.Action {
                case api.StoreActionKindCreate:
                    return CreateExtension(tx, obj)
                case api.StoreActionKindUpdate:
                    return UpdateExtension(tx, obj)
                case api.StoreActionKindRemove:
                    return DeleteExtension(tx, obj.ID)
                }
            }
            return errUnknownStoreAction
        },
    })
}

type extensionEntry struct {
    *api.Extension
}

func (e extensionEntry) CopyStoreObject() api.StoreObject {
    return extensionEntry{Extension: e.Extension.Copy()}
}

// ensure that when update events are emitted, we unwrap extensionEntry
func (e extensionEntry) EventUpdate(oldObject api.StoreObject) api.Event {
    if oldObject != nil {
        return api.EventUpdateExtension{Extension: e.Extension, OldExtension: oldObject.(extensionEntry).Extension}
    }
    return api.EventUpdateExtension{Extension: e.Extension}
}

// CreateExtension adds a new extension to the store.
// Returns ErrExist if the ID is already taken.
func CreateExtension(tx Tx, e *api.Extension) error {
    // Ensure the name is not already in use.
    if tx.lookup(tableExtension, indexName, strings.ToLower(e.Annotations.Name)) != nil {
        return ErrNameConflict
    }

    // It can't conflict with built-in kinds either.
    if _, ok := schema.Tables[e.Annotations.Name]; ok {
        return ErrNameConflict
    }

    return tx.create(tableExtension, extensionEntry{e})
}

// UpdateExtension updates an existing extension in the store.
// Returns ErrNotExist if the object doesn't exist.
func UpdateExtension(tx Tx, e *api.Extension) error {
    // TODO(aaronl): For the moment, extensions are immutable
    return errors.New("extensions are immutable")
}

// DeleteExtension removes an extension from the store.
// Returns ErrNotExist if the object doesn't exist.
func DeleteExtension(tx Tx, id string) error {
    e := tx.get(tableExtension, id)
    if e == nil {
        return ErrNotExist
    }

    resources, err := FindResources(tx, ByKind(e.(extensionEntry).Annotations.Name))
    if err != nil {
        return err
    }

    if len(resources) != 0 {
        return errors.New("cannot delete extension because objects of this type exist in the data store")
    }

    return tx.delete(tableExtension, id)
}

// GetExtension looks up an extension by ID.
// Returns nil if the object doesn't exist.
func GetExtension(tx ReadTx, id string) *api.Extension {
    e := tx.get(tableExtension, id)
    if e == nil {
        return nil
    }
    return e.(extensionEntry).Extension
}

// FindExtensions selects a set of extensions and returns them.
func FindExtensions(tx ReadTx, by By) ([]*api.Extension, error) {
    checkType := func(by By) error {
        switch by.(type) {
        case byIDPrefix, byName, byCustom, byCustomPrefix:
            return nil
        default:
            return ErrInvalidFindBy
        }
    }

    extensionList := []*api.Extension{}
    appendResult := func(o api.StoreObject) {
        extensionList = append(extensionList, o.(extensionEntry).Extension)
    }

    err := tx.find(tableExtension, by, checkType, appendResult)
    return extensionList, err
}

type extensionIndexerByID struct{}

func (indexer extensionIndexerByID) FromArgs(args ...interface{}) ([]byte, error) {
    return api.ExtensionIndexerByID{}.FromArgs(args...)
}
func (indexer extensionIndexerByID) PrefixFromArgs(args ...interface{}) ([]byte, error) {
    return api.ExtensionIndexerByID{}.PrefixFromArgs(args...)
}
func (indexer extensionIndexerByID) FromObject(obj interface{}) (bool, []byte, error) {
    return api.ExtensionIndexerByID{}.FromObject(obj.(extensionEntry).Extension)
}

type extensionIndexerByName struct{}

func (indexer extensionIndexerByName) FromArgs(args ...interface{}) ([]byte, error) {
    return api.ExtensionIndexerByName{}.FromArgs(args...)
}
func (indexer extensionIndexerByName) PrefixFromArgs(args ...interface{}) ([]byte, error) {
    return api.ExtensionIndexerByName{}.PrefixFromArgs(args...)
}
func (indexer extensionIndexerByName) FromObject(obj interface{}) (bool, []byte, error) {
    return api.ExtensionIndexerByName{}.FromObject(obj.(extensionEntry).Extension)
}

type extensionCustomIndexer struct{}

func (indexer extensionCustomIndexer) FromArgs(args ...interface{}) ([]byte, error) {
    return api.ExtensionCustomIndexer{}.FromArgs(args...)
}
func (indexer extensionCustomIndexer) PrefixFromArgs(args ...interface{}) ([]byte, error) {
    return api.ExtensionCustomIndexer{}.PrefixFromArgs(args...)
}
func (indexer extensionCustomIndexer) FromObject(obj interface{}) (bool, [][]byte, error) {
    return api.ExtensionCustomIndexer{}.FromObject(obj.(extensionEntry).Extension)
}